Rene KievitsandClaude Opus 5 ae8229588e tools: add read-only on-TV diagnostic dump
Run with: ssh root@<tv> 'sh -s' < tools/tv-diag.sh > tv-diag.txt

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-05 23:58:33 +02:00
2026-09-05 22:27:44 +02:00
2026-09-05 22:27:44 +02:00
2026-09-05 22:27:44 +02:00
2026-09-05 22:27:44 +02:00
2026-09-05 22:27:44 +02:00
2026-09-05 22:27:44 +02:00
2026-09-05 22:27:44 +02:00
2026-09-05 22:27:44 +02:00
2026-09-05 22:27:44 +02:00

LG Update Blocker

LG Update Blocker

A webOS homebrew app that gets rid of the "a new software version is available" popup LG shows on every boot — without touching the firmware itself, and fully reversible.

Built for a rooted LG CX (webOS 5), but nothing in it is model specific: it only ever probes for things and acts on what it actually finds on your TV.

screenshot

Why the popup keeps coming back

The Homebrew Channel's own Block system updates toggle only writes four hostnames into /etc/hosts at boot. That is not enough in practice:

  • it misses several update hostnames (nsu, *-ssl, the CDN aliases),
  • it does nothing about a firmware image the TV already downloaded — a staged update keeps prompting even when the update servers are unreachable,
  • it leaves the auto-update settings and the update services alone.

This app covers all of that, and re-applies everything on each boot (webOS resets /, /tmp and the mount namespace on every start).

Protection layers

Layer What it does Default
Block LG update servers Points snu/su/nsu.lge.com, their TLS variants, the -dev fallbacks and the CDN aliases at 127.0.0.1 in /etc/hosts on
Delete staged firmware Wipes the update image staged in /mnt/lg/cmn_data/swupdate on
Lock the staging folder Bind-mounts an empty read-only directory over it, so nothing can be staged again off
Turn off auto-update settings Reads every com.webos.settingsservice category, switches off each update-related key it finds, and remembers the original values on
Stop update services at boot Stops the update-related upstart jobs discovered in /etc/init (advanced) off

Every layer is undone by Remove protection, which also restores the settings from the backup it made.

Requirements

  • A rooted webOS TV with the Homebrew Channel installed and its root status showing ok.
  • The app's own service needs root. On first launch it asks the Homebrew Channel to elevate it (elevateService, falling back to running elevate-service through exec), then restarts itself. The header badge turns green when it worked.

Install

Through the Homebrew Channel (repository)

Add the repository once — Homebrew Channel → Settings → Add repository:

https://git.crylia.de/Crylia/lg_update_blocker/releases/download/latest/apps.json

The app then shows up in the Homebrew Channel list, and every new release that is tagged is picked up automatically — Gitea resolves .../download/latest/... to the newest release.

Manually

Download the .ipk from the release and:

ares-install com.rkievits.lgupdateblocker_1.0.0_all.ipk
ares-launch com.rkievits.lgupdateblocker

Using it

Navigate with ▲▼, select with OK, leave with BACK.

  1. Check the layers you want (the defaults are a good start).
  2. Apply protection — the log pane shows exactly what was changed.
  3. Reboot and confirm the popup is gone.

Other buttons: Purge staged update (wipe a downloaded image right now), Remove protection (undo everything), Refresh status, and Diagnostics (dumps OS info, mounts, discovered settings, the tail of /etc/hosts and the boot-hook log — handy for a bug report).

What it touches

Path Purpose
/etc/hosts Blocked hostnames, between # >>> lg-update-blocker >>> markers. Bind-mounted from /tmp/lgupdateblocker-hosts because / is read-only
/var/lib/webosbrew/init.d/lgupdateblocker Boot hook, run by the Homebrew Channel startup script; re-applies the enabled layers
/var/lib/webosbrew/lgupdateblocker/ config.json, hosts.txt, settings-backup.json, boot.log
/mnt/lg/cmn_data/swupdate Staged firmware, emptied (and optionally locked)

Nothing is written to system partitions, and no LG binaries are patched or replaced.

Troubleshooting

  • Badge says "no root" — open the Homebrew Channel, verify Root status is ok, then relaunch this app. If the Homebrew Channel itself is unelevated, fix that first (its Settings screen has a button for it).
  • Popup still appears after a reboot — run Diagnostics and look at the boot-hook log at the bottom. It records, per boot, whether the hosts entries were added, what was purged and which jobs were stopped.
  • Belt and braces — a hosts file only helps if the TV uses DNS. Blocking snu.lge.com, su.lge.com and nsu.lge.com on your router or Pi-hole as well is the one measure that also survives a factory reset.
  • Forced app updates (the "You must update this app to launch it" dialog) are a different mechanism — that is what LG App Update Blocker is for. This app deliberately leaves the lgtvsdp.com hosts alone, since blocking those also breaks the store and several other services.

Building

No bundler, no runtime dependencies — tools/build.js copies app/ and service/ into build/ and substitutes the app id and version.

npm install          # only the ares CLI
npm run dist         # build + package + manifest, output in dist/
npm run deploy       # ares-install the resulting ipk

Releasing

.github/workflows/release.yml builds on every push (Gitea Actions reads .github/workflows/ too). Tag a commit v<version> (matching version in package.json) and the workflow additionally publishes a release containing:

  • com.rkievits.lgupdateblocker_<version>_all.ipk
  • com.rkievits.lgupdateblocker.manifest.json — Homebrew Channel manifest, with the absolute ipk URL, its sha256 and size
  • apps.json — the one-package repository index used by the repository URL above
  • description.html — the long description shown in the Homebrew Channel

Uploading is done by tools/release-gitea.js against the Gitea API (softprops/action-gh-release only speaks to github.com). It is idempotent: re-running a tag reuses the release and replaces the attachments.

The URLs inside the manifest come from PUBLIC_SERVER_URL in the workflow, not from GITHUB_SERVER_URL — the runner is handed the instance's internal LAN address, which the TV cannot reach. Change that one variable if the instance moves. Generating for github.com instead works too: --server (or PUBLIC_SERVER_URL) switches the "latest release asset" URL to GitHub's shape.

The app id lives in exactly one place: the name field of package.json. Change it there and everything (appinfo, service name, Luna URIs, manifest) follows.

License

MIT

S
Description
No description provided
Readme MIT
456 KiB
v1.2.0
Latest
2026-09-06 02:18:22 +02:00
Languages
JavaScript 84.2%
Shell 10.1%
CSS 4.1%
HTML 1.6%