Skip to content

Instantly share code, notes, and snippets.

@Informatic
Last active March 30, 2024 15:59
Show Gist options
  • Star 68 You must be signed in to star a gist
  • Fork 9 You must be signed in to fork a gist
  • Save Informatic/1983f2e501444cf1cbd182e50820d6c1 to your computer and use it in GitHub Desktop.
Save Informatic/1983f2e501444cf1cbd182e50820d6c1 to your computer and use it in GitHub Desktop.
openlgtv webOS hacking notes

This is just a dump of some interesting undocumented features of webOS (3.8 specifically, on early 2018 4k LG TV) and other development-related tips.

Homebrew app ideas

Receiving DIAL/SSDP

Any installed application can receive DIAL start requests by adding "dialAppName": "NAME" in its appInfo.json. YouTube and Netflix apps have some special handling, but they still can be overriden by an unofficial app.

IMPORTANT: official YouTube app has "dialAppName": "youtube". Installed apps are (or rather seem to be) ordered lexicographically based on their IDs - when multiple apps declare the same dialAppName the last one will be the one triggered via DIAL. (com.youtube.adfree < youtube.leanback.v4 < youtube.leanforward)

TV Input embedding

Web apps can embed connected inputs surface in their DOM:

<video autoplay style="width:50%;height:50%">
  <source type="service/webos-external" src="ext://hdmi:1"></source>
</video>

Supported sources: ext://hdmi:1, ext://hdmi:2, ext://hdmi:3, ext://hdmi:4, ext://comp:1, ext://av:1, ext://av:2. Additionally TV stream can be embedded with src="tv://" and type="service/webos-broadcast"

Seems like only a single external input can be displayed at the same time. (at least on my TV, equivalent to multiview limitations)

Registering an app as an input (can be used to autostart an app)

Following needs to be added to appinfo.json:

  • "supportGIP": true
  • largeIcon needs to be set (otherwise eim just crashes)

When starting up an app following call needs to be executed to register it:

webOS.service.request("luna://com.webos.service.eim", {
  method: "addDevice",
  parameters: {
    "appId": "org.webosbrew.hbchannel", // your application ID, required
    "pigImage": "", // required, preview image rendered in "All inputs", relative to main application directory, can be just an empty string
    "mvpdIcon": "", // required on webOS <3.x
    "type": "MVPD_IP", // optional, no idea (can be MVPD_IP or MVPD_RF)
    "showPopup": true, // optional, shows a toast with info that default input has been changed to label
    "label": "application name", // optional, used in toast message only
    "description": "testing", // optional, description rendered in "All inputs"
  },
  onSuccess: function (res) { console.info('success:',res); },
  onFailure: function (res) { console.info('failure:', res); }
 });

luna://com.webos.service.eim/deleteDevice {"appId":"..."} call can be used to unregister an app.

This also works over luna-send-pub for any app, as long as its appinfo.json is properly set up, in case anyone wants to debug this manually. (source app ID is not verified)

Userscripts in apps

Userscript present in webOSUserScripts/userScript.js will be loaded as a userscript in app webviews / frames. (including frames in origins outside of app root)

Example app: https://github.com/FriedChickenButt/youtube-webos

WebAppMgr User-Agent change

Add the following to appinfo.json:

{
  "vendorExtension": {
    "userAgent":"$browserName$/$browserVersion$ ($platformName$-$platformVersion$), _TV_$chipSetName$/$firmwareVersion$ (LG, $modelName$, $networkMode$)"
  },
  "trustLevel": "netcast"
}

Note: No idea what trustLevel actually means - it seems to somewhat change supported web APIs and makes window.PalmServiceBridge unavailable. (launch params are in window.launchParams)

WebAppMgr - Disabling CORS enforcement

In order to disable CORS enforcement use:

{
  "vendorExtension": {
    "allowCrossDomain": true
  },
  "trustLevel": "netcast"
}

Invisible apps

Apps with "visible": false can be launched and have their services accessed, but they don't show up on main screen.

Note: LG Content Store is periodically queried for software updates of all installed applications (including homebrew/developer and system apps, app ids and versions are transmitted)

Multiview app

At least on my TV multiview doesn't seem to be officially supported anywhere in the UI. Yet still - it can be launched via telnet when rooted: /usr/bin/com.webos.app.multiview. This can possibly be wrapped in a proper app.

State monitoring

# Suspend / resume
luna-send -i 'luna://com.webos.service.tvpower/power/getPowerState' '{"subscribe":true}'

# Currently running application
luna-send -i 'luna://com.webos.applicationManager/getForegroundAppInfo' '{"subscribe":true}'

Injecting mitmproxy CA

Note: see Filesystem overlays below, /usr/share/ca-certificates/sdp + /etc/ssl/certs

mkdir -p /tmp/overlayfs/upper /tmp/overlayfs/work && mount -t overlay -o lowerdir=/etc/ssl/,upperdir=/tmp/overlayfs/upper/,workdir=/tmp/overlayfs/work/ overlay-ssl /etc/ssl/
cd /etc/ssl/certs
curl http://mitm.it/cert/pem > mitmproxy.crt
ln -s mitmproxy.crt $(openssl x509 -in /etc/ssl/certs/mitmproxy.crt -subject_hash_old -noout).0
cat mitmproxy.crt >> trusted_cas.crt
cat mitmproxy.crt >> ca-certificates.crt

# Also TODO: /usr/share/ca-certificates/sdp

Extra note: TVs have their own (Multiple) Client Keys (and certificates), that are used for mutual TLS authentication by some remote services. They are stored securely, and there's no way to Read them.

Disable "Wedge" / "My Content" / promotion bar on home screen

luna-send -n 1 -f luna://com.webos.service.config/setConfigs '{"configs":{"com.webos.service.favoriteservice.enableWedge": false}}'`

(note: this only persists, if *.lgtvsdp.com traffic is blocked)

Increase pmlogd logging

By default pmlogdaemon logs only topics listed in /etc/PmLogDaemon/whitelist.txt. To enable all logging use:

luna-send -n 1 -f 'luna://com.webos.pmlogd/setdevlogstatus' '{"recordDevLogs":false}'
luna-send -n 1 -f 'luna://com.webos.pmlogd/getdevlogstatus' '{}'

# After enabling all devlogs specific contexts can be adjusted using PmLogCtl
PmLogCtl set '*' 'debug'

# Newer webOS:
luna-send -n 1 -f luna://com.webos.service.config/setConfigs '{"configs": {"system.collectDevLogs": true}}'
luna-send -n 1 -f luna://com.webos.service.config/getConfigs '{"configNames":["system.collectDevLogs"]}'

(note: you most probably want to disable rdxd first, as per rootmytv immutable mounts...) (also: i got a tip this doesn't work anymore on newer webos/pmlogd versions)

Luna Service Bus debugging

# Log traffic
ls-monitor

# List connected clients
ls-monitor -l

# Inspect supported requests
ls-monitor -i com.webos.service...

# Some requests schemas are present in /etc/palm/schemas

Magic Remote IR blaster

luna-send -a 'important-dunno-why-lol' -n 1 -f 'luna://com.webos.service.mrcu/ir/pushIrCodes' '{ "irCodes": "0xaa 0x00 0x00 0x00 0x0c 0x94 0x00 0x00 0x58 0x0f 0x02 0x00 0xa2 0x00 0x07 0x81 0x13 0x03 0x07 0x81 0x13 0x03 0x07 0x81 0x2d 0x07 0x07 0x81 0x13 0x03 0x07 0x81 0x13 0x03 0x07 0x81 0x2d 0x07 0x07 0x81 0x13 0x03 0x07 0x81 0x2d 0x07 0x07 0x81 0x13 0x03 0x07 0x81 0x2d 0x07 0x07 0x81 0x13 0x03 0x07 0x81 0x13 0x03 0x07 0x81 0x13 0x03 0x07 0x81 0x2d 0x07 0x07 0x81 0x13 0x03 0x07 0x81 0x2d 0x07 0x1a 0x04 0x1a 0x04 0x1a 0x04 0x1a 0x04 0x1a 0x04 0x1a 0x04 0x07 0x81 0x13 0x03 0x07 0x81 0x13 0x03 0x07 0x81 0x2d 0x07 0x07 0x81 0x13 0x03 0x07 0x81 0x2d 0x07 0x07 0x81 0x13 0x03 0x07 0x81 0x2d 0x07 0x07 0x81 0x13 0x03 0x07 0x81 0x13 0x03 0x07 0x81 0x13 0x03 0x07 0x81 0x2d 0x07 0x07 0x81 0x13 0x03 0x07 0x81 0x2d 0x07 0x1a 0x04 0x1a 0x04 0x1a 0x04 0x1a 0x04 0x1a 0x04 0x1a 0x07 0x07 0x81 0x2d 0x07 0x07 0x81 0x2d 0x07 0x07 0x81 0x13 0x03 0x07 0x81 0x2d 0x07 0x07 0x81 0x13 0x03 ", "irCodeCount": 174 }'

todo: research what's the actual encoding scheme

When enabling magic remote IR control of appliances LG's services are accessed to download new keycodes. These are then stored in /mnt/lg/user/irdbmanager (oss_audio_append_db.txt and setting/oss_setting_info_audio.txt). Some default codes are also present in /usr/share/irdbmanager.

Nice potentially hackable apps

  • My starter (triggered by pressing clock on home screen) → /usr/palm/applications/com.webos.app.mystarter/qml/main.qml
  • Screensaver (fireworks) → /usr/palm/applications/com.webos.app.screensaver/qml/main.qml
  • Home screen layout → seems like all .qml files are bundled in surface-manager-starfish (managed to extract it without filenames with binwalk - someone needs to make some qt resources unpacker) - but it also accepts an env variable with custom .qml startup file.

Custom Magic Remote cursors

Cursors are rendered by surface-manager-starfish (surface-manager upstart service), and are loaded from /usr/share/im. cursorTypeAszMstN.png is the default "idle" medium-sized cursor. S/M/L in that filename is cursor size. /usr/share/im needs to be mounted/overlaid early on in the boot process (needs testing), or surface-manager needs to be manually restarted after applying the patch. Stays on between suspends when Quick Start+ is enabled. Filesystem overlay like below can be used.

Filesystem overlays

Use https://gist.github.com/Informatic/db387d512bf4ae5512d9f644c4d219d2

Download and install an ipk in (root) shell

curl -L https://github.com/mariotaku/moonlight-tv/releases/download/v0.6.0/com.limelight.webos_0.6.0_arm.ipk -o /media/internal/downloads/app.ipk && \
luna-send -i -f luna://com.webos.appInstallService/dev/install '{"id":"com.limelight.webos","ipkUrl":"/media/internal/downloads/app.ipk","subscribe":true}'

App launch via shell

luna-send -n 1 -f luna://com.webos.service.applicationManager/launch '{"id":"com.webos.app.screensaver"}' 

Screenshot

luna-send -n 1 -f 'luna://com.webos.service.tv.capture/executeOneShot' '{"path":"/tmp/capture.png","method":"DISPLAY","format":"PNG"}'
# Supported formats: BMP, JPG, PNG, RGB, RGBA, YUV422
# Supported methods: SCREEN/DISPLAY (alias?), SCREEN_WITH_SOURCE_VIDEO, VIDEO, GRAPHIC, SOURCE/SCALER (alias?)

# Note: com.webos.service.tv.capture above has been renamed to com.webos.service.capture on some devices, call signature is the same.

(Note: this is also available over SSAP as: ssap://tv/executeOneShot - returns imageUri attribute with HTTP link)

Factory mode / instart / instop...

Don't fuck with this. You WILL most probably brick your TV.

# -> binwalk -e /usr/bin/surface-manager-starfish && grep -Ri factory *.extracted
luna-send -n 1 -f luna://com.webos.service.applicationManager/launch '{"id":"com.webos.app.factorywin","params":{"id":"executeFactory","irKey":"inStart"}}'
# Alternative irKeys: powerOnly, inStart, ezAdjust, inStop, pCheck, sCheck, tilt

# ezAdjust is service menu 1
# inStart is service menu 2
# inStop is factory reset (?)
# pCheck will override some picture settings for lcd testing
# sCheck will boost sound to test audio
# tilt opens white screen that does nothing and locks remote buttons, goes away after a reboot

# Also, rather obviously, this can be triggered over SSAP as well... :)

# -> /usr/palm/applications/com.webos.app.factorywin
# Password: 0413
# Bang & Olufsen password: 1925
# "USB Log" (?) password: 1126

"Host Diagnostics" view

Go to settings → Programmes → Hover "Programme Tuning & Settings" and press "1" button five times.

Disable 15 minute no-signal auto power off

luna-send -n 1 luna://com.webos.settingsservice/setSystemSettings '{"settings":{"autoOff15Min":"off"},"category":"time"}'

Accessing devmode SSH without full webOS SDK

tv=10.11.12.13
# 1. Enable devmode
# 2. Enable keyserver
# 3. Download key and decrypt using Passphrase displayed on a screen
openssl rsa -in <(curl http://$tv:9991/webos_rsa) > webos_rsa && chmod 600 webos_rsa
# 4. just ssh
ssh prisoner@$tv -i webos_rsa -p 9922 bash -i

Tip: @webosose/ares-cli NPM package is sufficient to play with webOS TV development. (ares-package, ares-install, ares-launch...) A quick tip on how to configure everything is available here: FriedChickenButt/youtube-webos#13 (comment)

Using sshd after deploying RootMyTV

Note: dropbear sshd is now bundled by default with Homebrew Channel app. If you've rooted Your TV using https://rootmy.tv you may already have it, you just need to enable it in Homebrew Channel Settings view.

Run this manually in telnet session:

mkdir -p /var/run/sshd && /media/cryptofs/apps/usr/palm/services/com.palmdts.devmode.service/binaries-armv71/opt/openssh/sbin/sshd -o StrictModes=no -f /media/cryptofs/apps/usr/palm/services/com.palmdts.devmode.service/binaries-armv71/opt/openssh/etc/ssh/sshd_config -h /var/lib/devmode/sshd/ssh_host_rsa_key -o PasswordAuthentication=yes -o PermitRootLogin=yes -o PermitUserEnvironment=yes -o AuthorizedKeysFile=/media/developer/.ssh/authorized_keys -D -p 9922

Afterwards, SDK needs to be reconfigured to use root username:

ares-setup-device -m DEVICE -i "username=root"

SSH public key can be added to: /media/developer/.ssh/authorized_keys

Inspector for development apps is present on: http://TV_IP:9998

Running native app in jailer

jailer -d -t native_devmode -p /media/developer/apps/usr/palm/applications/com.limelight.webos/ -i com.limelight.webos $(which id)

Controlling screen backlight

luna-send -n 1 "luna://com.webos.service.tvpower/power/turnOnScreen" '{}'
luna-send -n 1 "luna://com.webos.service.tvpower/power/turnOffScreen" '{}'
luna-send -n 1 'luna://com.webos.settingsservice/setSystemSettings' '{"category":"picture","settings":{"energySaving":"screen_off"}}'

luna-send-pub -f -n 1 luna://com.webos.service.applicationManager/launch '{"id":"com.webos.app.tvhotkey","params":{"activateType":"energy-saving-mode"}}'
@Wouterdek
Copy link

This is useful, thanks!

@NguyenASang
Copy link

Anyway to remove limit developer time on LG TV ?

@KenMacD
Copy link

KenMacD commented Sep 10, 2021

@NguyenASang You can use the info in my gemini post on it. You want to edit the file /media/cryptofs/apps/usr/palm/services/com.palmdts.devmode.service/start-devmode.sh:

Devmode has a shutdown counter that will cause it to be disabled after a certain amount of time. Further down in the script we can find a line saying that under some conditions devmode should be kept. The line looks like:

# If LGERP app (com.lgerp.appinstaller) is installed, devmode should be kept.
if [ -d ${LGERP_APPINSTALLER_DIR} ]; then

This can be converted to always run instead:

if true; then

@Informatic
Copy link
Author

Informatic commented Sep 10, 2021

If you are going to root - why not just use https://rootmy.tv which will deploy Homebrew Channel-shipped startup script? Our defaults are more-or-less sensible - no timers, proper sshd (that can be used with SDK tools, with working scp/rsync), etc...

@NguyenASang
Copy link

Do you mean when I root my TV, the developer time limit will disappear :D
It will be very cool :))

@ledoge
Copy link

ledoge commented Sep 24, 2021

This command worked on a UF7600 (webOS 2.0) to open the service menu:
luna-send-pub -n 1 -f luna://com.webos.applicationManager/launch '{"id":"com.webos.app.livetv","params":{"id":"executeFactory","irKey":"ezAdjust"}}'

@frederikdebruyker
Copy link

Any way to replace the existing screen saver images?

@Informatic
Copy link
Author

/usr/palm/applications/com.webos.app.inputcommon/qml/Model/ScreensaverPhotoList.js is a JS file that contains a function that generates a list of photos to cycle through for no-signal images.

You can either look through the paths it tries to use and bind mount over those, or create your own custom file that would replace that (it just needs to expose function getImgList(panel)), roughly like this:

echo 'function getImgList(panel) { return ["/home/root/image1.jpg", "/home/root/image2.png"]; }' > /home/root/customphotos.js
mount --bind /home/root/customphotos.js /usr/palm/applications/com.webos.app.inputcommon/qml/Model/ScreensaverPhotoList.js

@kopiro
Copy link

kopiro commented May 6, 2022

Hey @Informatic , any way to add cursors? I see they're in /usr/share/im, but I would rather add than replace it

@zipperhead46237
Copy link

Any way to replace the existing screen saver images?

Any way to just turn off the screensaver? or even change the timeout to a longer period before it comes on? I use streaming apps that have their very own screensavers built in and they are much more pleasant the the LG screensaver.

@zipperhead46237
Copy link

I would love to be able to send a command to turn off or disable the screensaver. and then be able to re-enable after being finished using streaming apps.

@tuxuser
Copy link

tuxuser commented May 30, 2022

To fetch some system infos:

luna-send -f -n 1 'luna://com.webos.service.systemservice/osInfo/query' '{"parameters":["webos_manufacturing_version", "webos_release", "webos_build_datetime"]}'

Response:

{
    "webos_release": "4.4.0",
    "webos_build_datetime": "20211230050539",
    "webos_manufacturing_version": "05.40.26",
    "returnValue": true
}

@oliv615
Copy link

oliv615 commented Dec 28, 2022

TV stream can be embedded with src="tv://"
How to choose the channel to play?
Thank you

@hanovof0811
Copy link

Help Injecting mitmproxy CA
I got this error:
root@LGwebOSTV:/etc/ssl/certs# ln -s mitmproxy.crt $(openssl x509 -in /etc/ssl/c
erts/mitmproxy.crt -subject_hash_old -noout).0
unable to load certificate
2532467920:error:0906D06C:PEM routines:PEM_read_bio:no start line:pem_lib.c:707:Expecting: TRUSTED CERTIFICATE

Anyone have solusion? Thanks.

@jonferreira
Copy link

Hey.
Any way to enable USB Log?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment