# 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`:
```html
MyApp
MyApp
Läuft im hisense_browser (root, --allow-file-access-from-files).
```
- HTTP-Variante: Seite auf dem Host (`http://: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`) und `Hisense_*` nutzen. Root-Kontext.
### A.2 `Appinfo.json` ergänzen
Datei: `/3rd_rw/appdata/launcher/Appinfo.json` (rw). **Backup:**
```sh
cp /3rd_rw/appdata/launcher/Appinfo.json /3rd_rw/backup/Appinfo.json.$(date +%s)
```
Eintrag ergänzen (bestehende `AppInfo`-Einträge behalten!):
```json
{"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 oder `file://`.
- `Image`/`IconURL`/`Thumb`/`Icon_96` = Icon; HTTP/`file://`; Fallback `useDefaultImg`.
- `StoreType` als String (`"foxxum"`; die UI mappt über `storeTypeList`).
- `PreInstall:false` (User-App); `canRemove=true`.
**Per TV-Seite/JS** (ohne SSH):
```js
// 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 `; `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.txt` für User-Apps missbrauchen; der `preset`-Cache kann überschrieben werden. `Appinfo.json` ist 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
```sh
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`.
```html
My TV
My TV
```
- **API-Verfügbarkeit ist `UNVERIFIED`**: Im UI-DevTools (9223) prüfen, welche Globals existieren und welche Signatur `Hisense_StartApp`/`TVAPI.StartApp`/`guijs.StartApp` hat. Der Web-Browser hat `HiBrowser.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 viewport` nutzen.
### B.3 Testen, ohne die UI zu ersetzen
1. `custom.html` per Web-Browser öffnen: `http://:9000/...` oder `file:///3rd_rw/UI/custom.html` im **`hisense_browser`** (TV-Browser). Dort `HiBrowser`/`Hisense_*` testen (die UI-spezifischen APIs fehlen evtl.).
2. 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 **neuen** `hisenseUI`-Prozess starten (siehe B.5) oder per Web-Browser testen.
3. Wenn die Custom-Seite im Web-Browser gut aussieht, UI übernehmen.
### B.4 `index.html` ersetzen
**Variante 1: Datei ersetzen** (einfach, reversibel per Backup):
```sh
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):
```sh
# /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):
```sh
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 bind` ersetzt `/3rd/opera4x` komplett. Fehlende Dateien = Browser/UI kaputt.
- Vorteil: überlebt Reboots (autorun bindet vor UI-Start). Nachteil: groß/fehleranfällig.
### B.5 UI-Restart / Rettung
```sh
# UI-Prozess
ps | grep hisenseUI | grep -v grep
kill
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.html` liegen 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`.
```json
[
"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.js` filtert die Kacheln; die `preset.txt` ist 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.txt` ergänzen (`AppName`, `IconURL`, `URL`, `StartCommand`, `UrlType` 37, `StoreType` 0, `PreInstall:true`). **Aber:** `preset.txt` ist ein Cache; die UI/`allAppsWriteToFileForUse` kann sie überschreiben. Für dauerhaft: `Appinfo.json` (User-App) oder die `Category*.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 zu `DEU` passt, nutzt `ldi.js` `UI/data/`-Default. Für eigene Daten ggf. `UI/data/` bearbeiten (Fallback) oder `serviceUpdate`/`Category` in `launcher/data` konsistent halten.
### C.3 Icons / Skin
- Icons: `res/*.png`; `cache/index.json`; `Image`/`IconURL`/`Thumb`/`Icon_96`; `useDefaultImg` als 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). Die `launcher`/`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.txt` ist 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`.
```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.sh` wirken für die späteren Zeilen.
- **Log:** `/3rd_rw/developer.log`; `set -x` optional.
- **Fallback:** SSH/Root-Loop; wenn `developer.sh` die 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):
```sh
#!/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 `HamService` läuft weiter.
- **ACR abschalten:** `exec /3rd/hamservice/HamService "$@"` durch `exit 0` ersetzen (Root-Loop/SSH bleiben); oder ACR im HamService-Config deaktivieren (UNVERIFIED).
- **Eigene späte Services:** vor dem `exec` starten (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.sh` liest `root_cmd.sh`, schreibt `root_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,,:start=")` im Web-Browser (belegt).
- `:am,,:URL=` (aus `app_man`-Strings); `:stop`, `:backgm`, `:foregm`, `:pause`, `:resume`, `:shutdown`, `:suspend`, `:wakeup`.
- **Neue native App** braucht einen `am_config`-Eintrag (`exec`/`args`) — ro; per `developer.sh`-Bind-Mount einer modifizierten `am_config` (vor `app_man`). Oder ein eigener Service über `developer.sh`/`HamService` (kein AM nötig).
- **`Hisense_StartApp`** in 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_man` für TV-Funktionen; `libhspdk-jsx`/`HiBrowser` fü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)
```sh
# 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 vor `ms_service`/UI/`app_man`/`delayrun`.
- **Reboot-fest:** `developer.sh` fü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`) per `mount --bind` über die Binärdatei; der `-x`-Check läuft danach ins Leere.
- **Achtung:** `mount --bind` auf eine Datei benötigt das Ziel; die ro-Datei existiert. Kein Overlay nötig.
### E.2 `patch/opera4x` (full overlay)
- `autorun.sh` bindet `/3rd_rw/patch/opera4x` **als 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, per `developer.sh` binden.
- **Vor** `app_man` (Zeile 177); `developer.sh` (90).
- **Risiko:** `dtv_app_mtk`/`app_man` nicht entfernen (TV/Apps). `netflix`/`dialserver`/`samba`/`browser_base`/`securetime` optional (siehe `05`).
### E.4 `delayrun.sh`
- Kopie nach `/3rd_rw/etc/delayrun.sh`; unerwünschte Zeilen entfernen; per `developer.sh` über `/basic/delayrun.sh` binden.
- **Vor** `autorun` Zeile 190; `developer.sh` (90).
- **Risiko:** `LauncherService` (UI-Daten) und `HamService` (unsere Persistenz) behalten; `swrc`/`runlog`/`Seriallog` optional.
---
## Route F — Statische Tools / Deploy / SSH
- **Kein Package Manager** (siehe `06`): statische ARM-Binaries bauen, nach `/3rd_rw/bin` legen, 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:
```bash
sftp root@192.168.188.94
# put -r /lokaler/UI-Ordner /3rd_rw/
# get /3rd_rw/UI/index.html
```
Modernes `scp` (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
1. **Backup:** `/3rd_rw/backup//` 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).
2. **Eine Änderung:** nicht UI + AM + Bloat gleichzeitig.
3. **Test:** UI neu starten; SSH/Recon offen halten; `log`/DevTools prüfen.
4. **Rollback:** Backup zurück; `umount` der Bind-Mounts; UI/AM neu starten; notfalls reboot.
5. **Kein Flash:** alle Änderungen in `/3rd_rw`/`/data`/`/perm`/Bind-Mounts; der ro Rootfs/`/3rd` bleibt unangetastet.
6. **SSH/Root-Loop:** nie aussperren; vor UI-/AM-Änderungen testen, dass SSH noch geht.
---
## Templates
- `templates/developer.sh`
- `templates/custom-app.json`
- `templates/hide-preset.json`
- `templates/custom-ui.html`
- `templates/start-UI-custom.sh`
- `templates/README.md`
**Dead Ends / verworfene Wege:** `../dead-ends.md`.