Full-featured ioBroker adapter for LG WebOS Smart TVs — control picture mode, sound mode, inputs, volume, remote control and more.
Designed as a replacement for homebridge-lgwebos-tv, tested on LG OLED77G4 (webOS 24).
- 🖼️ Picture Mode — read & write (Vivid, Cinema, Filmmaker, Expert, HDR, Dolby Vision…)
- 🎚️ Picture Settings — Brightness, Contrast, Backlight / OLED Light, Color, Sharpness
- 🔊 Sound Mode — read & write (Standard, Cinema, AI Sound Pro…)
- 🔉 Audio Output — switch between TV Speaker, HDMI ARC/eARC, Optical, Bluetooth Soundbar
- 📺 Input switching — HDMI 1–4 and all other inputs
- 📡 TV Channels — switch by number, see channel name and full list
- 📱 App launcher — launch any webOS app by ID (Netflix, YouTube, Disney+…)
- 🎮 Full remote control — all physical buttons simulated via ioBroker
- 💡 Screen off / Screen saver — without powering off the TV
- ⏯️ Media state — detect playback status
- 🌐 Wake-on-LAN — turn the TV on remotely
- 🔢 Numeric states — numeric mirror for every string state (perfect for MQTT / Loxone)
- 🔔 Real-time push — picture & sound changes on TV are reflected instantly in ioBroker (no polling needed)
- 🔄 Auto-reconnect — automatic reconnection if TV is turned off or goes to standby
| Requirement | Details |
|---|---|
| ioBroker | js-controller ≥ 3.0 |
| Node.js | ≥ 14 |
| LG WebOS TV | webOS 4+ (fully tested on webOS 24 / LG G4) |
| Network | TV and ioBroker on the same local network |
| Static IP | Set a DHCP reservation for the TV in your router |
- Open ioBroker Admin → Adapters
- Click the GitHub icon (install from custom URL)
- Enter the URL:
https://github.com/KPIotr89/iobroker.lgtv-full - Click Install and wait for completion
- Open the instance settings and configure the TV IP address and MAC address
To receive automatic update notifications in ioBroker Admin:
- Go to Settings → Repositories
- Add a new entry with this URL:
https://raw.githubusercontent.com/KPIotr89/iobroker.lgtv-full/main/repository.json - Enable Auto-Upgrade — ioBroker will notify you (or upgrade automatically) when a new version is published
Open the adapter instance settings in ioBroker Admin:
| Field | Example | Description |
|---|---|---|
| TV IP Address | 192.168.1.105 |
Local IP of the TV — assign a DHCP reservation in your router |
| MAC Address | A1:B2:C3:D4:E5:F6 |
Required for Wake-on-LAN. Found in TV: Settings → Connection → Network → Advanced |
| Reconnect interval | 10 |
Seconds between reconnect attempts when TV is off |
On the first run, the TV will display a pairing prompt on screen. Accept it with the remote control. The pairing key is saved automatically and pairing is only required once.
Note: If you see a pairing prompt every restart, delete
lgtvkey.txtfrom the instance data directory and let it re-pair once.
| State | Type | R/W | Description |
|---|---|---|---|
power |
boolean | R/W | true = turn on via WoL / false = turn off |
screenOff |
boolean | R/W | Turn the screen off without standby (panel goes dark, TV stays on) |
screenSaver |
boolean | R | true when screen saver is active |
info.connection |
boolean | R | true when adapter is connected to the TV |
| State | Type | R/W | Description |
|---|---|---|---|
audio.volume |
number (0–100) | R/W | Volume level in percent |
audio.mute |
boolean | R/W | Mute on/off |
audio.soundMode |
string | R/W | Sound mode key (e.g. cinema, aiSoundPro) |
audio.soundModeNum |
number | R/W | Sound mode as number — see table below |
audio.soundOutput |
string | R/W | Audio output key (e.g. tv_speaker, external_arc) |
audio.soundOutputNum |
number | R/W | Audio output as number — see table below |
Sound Modes:
| # | Key | Label |
|---|---|---|
| 1 | standard |
Standard |
| 2 | music |
Music |
| 3 | cinema |
Cinema |
| 4 | sport |
Sport / Stadium |
| 5 | game |
Game |
| 6 | aiSound |
AI Sound |
| 7 | aiSoundPro |
AI Sound Pro |
Sound Outputs:
| # | Key | Label |
|---|---|---|
| 1 | tv_speaker |
TV Speaker |
| 2 | external_arc |
HDMI ARC / eARC |
| 3 | external_optical |
Optical Out |
| 4 | bt_soundbar |
Bluetooth Soundbar |
| 5 | headphone |
Headphone |
| 6 | lineout |
Line Out |
| 7 | tv_external_speaker |
TV + External Speaker |
| State | Type | R/W | Description |
|---|---|---|---|
picture.mode |
string | R/W | Picture mode key (e.g. cinema, filmMaker) |
picture.modeNum |
number | R/W | Picture mode as number — see table below |
picture.brightness |
number (0–100) | R/W | Brightness |
picture.contrast |
number (0–100) | R/W | Contrast |
picture.backlight |
number (0–100) | R/W | Backlight / OLED Light intensity |
picture.color |
number (0–100) | R/W | Color saturation |
picture.sharpness |
number (0–50) | R/W | Sharpness |
Picture Modes full list (click to expand)
| # | Key | Label |
|---|---|---|
| 1 | vivid |
Vivid |
| 2 | standard |
Standard |
| 3 | eco |
Eco / APS |
| 4 | cinema |
Cinema |
| 5 | sport |
Sport |
| 6 | game |
Game |
| 7 | filmMaker |
Filmmaker Mode |
| 8 | expert1 |
Expert (Bright Room) |
| 9 | expert2 |
Expert (Dark Room) |
| 10 | hdrVivid |
HDR Vivid |
| 11 | hdrStandard |
HDR Standard |
| 12 | hdrCinema |
HDR Cinema |
| 13 | hdrFilmMaker |
HDR Filmmaker |
| 14 | hdrGame |
HDR Game |
| 15 | hdrSport |
HDR Sport |
| 16 | hdrCinemaBright |
HDR Cinema Home |
| 17 | dolbyHdrVivid |
Dolby Vision Vivid |
| 18 | dolbyHdrStandard |
Dolby Vision Standard |
| 19 | dolbyHdrCinema |
Dolby Vision Cinema |
| 20 | dolbyHdrCinemaBright |
Dolby Vision Cinema Bright |
| 21 | dolbyHdrFilmMaker |
Dolby Vision Filmmaker |
| 22 | dolbyHdrGame |
Dolby Vision Game |
ℹ️ HDR and Dolby Vision modes are only available when the TV receives an HDR/DV signal. The TV will ignore the command if the source doesn't match.
| State | Type | R/W | Description |
|---|---|---|---|
input.current |
string | R/W | Active input ID — write to switch source |
input.list |
JSON | R | All available inputs as a JSON object |
Input IDs are the webOS app IDs (the exact set depends on your TV / connected devices). Typical HDMI IDs:
| Input | ID |
|---|---|
| HDMI 1 | com.webos.app.hdmi1 |
| HDMI 2 | com.webos.app.hdmi2 |
| HDMI 3 | com.webos.app.hdmi3 |
| HDMI 4 | com.webos.app.hdmi4 |
| Live TV | com.webos.app.livetv |
Read input.list for the exact IDs and labels reported by your TV.
| State | Type | R/W | Description |
|---|---|---|---|
channel.number |
string | R/W | Current channel number — write to switch |
channel.name |
string | R | Current channel name |
channel.list |
JSON | R | Full channel map { "number": "name" } |
| State | Type | R/W | Description |
|---|---|---|---|
notify |
string | W | Write text to show a native toast on the TV screen (top corner, auto-dismisses ~5 s) |
Uses createToast — no popup issue on webOS 24. Publish non-retained (impulse), so an old message doesn't reappear when the TV turns on. Bypasses command deduplication: the same text twice shows two toasts.
| State | Type | R/W | Description |
|---|---|---|---|
app.current |
string | R | Currently running app ID |
app.launch |
string | W | Write an app ID to launch it |
Common app IDs:
| App | ID |
|---|---|
| Netflix | netflix |
| YouTube | youtube.leanback.v4 |
| Amazon Prime Video | amazon |
| Disney+ | disneyplus |
| Spotify | spotify-beehive |
| Apple TV | com.apple.appletv |
| Web Browser | com.webos.app.browser |
| Live TV | com.webos.app.livetv |
| LG Content Store | com.webos.app.lgappstv |
All remote buttons are exposed as writable boolean states under remote.*.
Set any button to true to simulate a press — it resets to false automatically.
| Category | Buttons |
|---|---|
| Navigation | LEFT RIGHT UP DOWN OK |
| System | HOME BACK MENU EXIT INFO GUIDE MYAPPS |
| Colour | RED GREEN YELLOW BLUE |
| Volume | VOLUMEUP VOLUMEDOWN MUTE |
| Channels | CHANNELUP CHANNELDOWN |
| Media | PLAY PAUSE STOP FASTFORWARD REWIND |
| Numbers | 0 1 2 3 4 5 6 7 8 9 |
| Other | DASH ENTER CC QMENU ASPECT_RATIO RECENT SEARCH |
| Streaming | NETFLIX AMAZON DISNEY |
Every string state (picture mode, sound mode, sound output) has a numeric mirror state ending in Num.
Both are always in sync — write to either one and the other updates automatically.
This makes integration with systems that prefer numbers (MQTT, Loxone, KNX) straightforward:
picture.mode = "cinema" ←→ picture.modeNum = 4
audio.soundMode = "aiSound" ←→ audio.soundModeNum = 6
audio.soundOutput = "external_arc" ←→ audio.soundOutputNum = 2
Since v1.2.0 the adapter includes a built-in MQTT client — no separate ioBroker MQTT adapter needed.
Open the adapter instance settings and fill in the MQTT Integration section:
| Field | Default | Description |
|---|---|---|
| Enable MQTT client | off | Toggle to activate |
| Broker Host | — | IP or hostname of your MQTT broker (e.g. 192.168.1.10) |
| Broker Port | 1883 |
Standard MQTT port |
| Topic prefix | lgtv |
Base topic for all messages |
| Username | — | Optional broker authentication |
| Password | — | Optional broker authentication |
All MQTT fields are hidden when the client is disabled.
| Direction | Topic pattern | Example |
|---|---|---|
| TV → broker (publish) | {prefix}/state/{name} |
lgtv/state/picture/mode → cinema |
| Broker → TV (subscribe) | {prefix}/set/{name} |
lgtv/set/picture/mode ← filmMaker |
All published values use / as separator and are retained on the broker, so a newly connected client always gets the current state immediately.
lgtv/state/info/connection → true / false
lgtv/state/audio/volume → 45
lgtv/state/audio/soundMode → cinema
lgtv/state/audio/soundModeNum → 3
lgtv/state/picture/mode → filmMaker
lgtv/state/picture/modeNum → 7
lgtv/state/picture/backlight → 30
lgtv/state/input/current → com.webos.app.hdmi3
lgtv/set/audio/volume ← 50
lgtv/set/picture/mode ← cinema
lgtv/set/picture/modeNum ← 4
lgtv/set/picture/backlight ← 60
lgtv/set/audio/soundMode ← aiSoundPro
lgtv/set/input/current ← com.webos.app.hdmi3
lgtv/set/power ← true / false
lgtv/set/notify ← Someone at the gate 🔔
notifyshows a native webOS toast in the corner of the screen (auto-dismisses after ~5 s). It bypasses command deduplication — publishing the same text twice shows two toasts. Publish non-retained so old notifications don't pop up when the TV turns on.
Point the adapter to your Mosquitto broker running on LoxBerry. In Loxone Config, create Virtual Inputs and Virtual Outputs mapped to the topics above — no scripting required.
For systems that prefer numbers, use the Num variants:
picture.modeNum 1–22 (see picture modes table)
audio.soundModeNum 1–7
audio.soundOutputNum 1–7
TV does not respond to Wake-on-LAN
- Verify the MAC address in TV: Settings → Connection → Network → Wi-Fi / Wired → View Details
- Enable "Turn on via Wi-Fi" in TV: Settings → General → External Devices → Quick Start+
- The TV must be in standby (not fully unplugged) for WoL to work
Pairing prompt appears on every restart
The pairing key file was not saved correctly. Check that the adapter has write access to its data directory. If the file exists but is empty, delete it and restart:
rm /opt/iobroker/iobroker-data/lgtv-full.0/lgtvkey.txtThen restart the adapter and accept the pairing prompt on the TV.
401 insufficient permissions on writes
This means the adapter was paired with an older manifest that didn't include WRITE_SETTINGS. To fix:
- Stop the adapter
- Delete the key file:
rm /opt/iobroker/iobroker-data/lgtv-full.0/lgtvkey.txt - Start the adapter
- Accept the pairing prompt on the TV
"Unknown message OK" popup appears when changing picture / sound mode (webOS 24)
This popup was shown by webOS 24 (LG G4 and newer) when an external app called ssap://settings/setSystemSettings directly. The adapter uses a different approach since v1.2.20: it triggers settings via a Luna service callback inside createAlert, bypassing the popup entirely. If you still see the popup, make sure you are running v1.2.29 or later. Check the debug log for createAlert cb: alertId= — if you see alertId=none the createAlert call failed and the adapter fell back to direct SSAP.
Picture mode does not change (HDR / Dolby Vision modes)
HDR and Dolby Vision picture modes are only available when the TV receives a compatible signal (e.g. HDR10 or Dolby Vision content from HDMI). The TV will silently ignore the command if the current source doesn't support that mode. This is a TV firmware limitation, not an adapter issue.
Some states show null / not updating
Check the debug logs in ioBroker Admin (set log level to debug). Common causes:
- TV is in standby — states will update after the TV wakes up
- Subscription failed — the adapter will fall back to polling every 60 seconds
- Incorrect setting keys for your TV firmware — post an issue with your debug log
- Fix:
powerstayed at1with the screen off — with Quick Start+ the SSAP socket stays alive in standby, so a socket close never happened and power was never reported as off - Now subscribes to
ssap://com.webos.service.tvpower/power/getPowerStateand maps the real state:Active/Screen Saver/Screen Off→ on,Active Standby/Suspend/Power Off→ off - Updates
powerinstantly (pushed by the TV) — much faster than waiting for the socket to drop; same approach ashomebridge-lgwebos-tv - Falls back to socket-based detection if the endpoint errors on older webOS
- Fix: Input switching never worked —
ssap://tv/switchInputexpects the device id (e.g.HDMI_3) but the adapter passed the appId (com.webos.app.hdmi3), so the TV ignored it - Additionally, the optimistic cache write then made every later input command be skipped as a duplicate
- Now switches by launching the input as an app (
system.launcher/launch), with aswitchInputfallback using the resolved device id input.currentis synced from the TV's foreground app, so the dedup cache tracks the real source (and reflects manual input changes via remote)- Reminder: the correct MQTT topic is
lgtv/set/input/current(notlgtv/set/input)
- Fix: Picture-mode dictionary had a non-existent key
hdrCinemaHome— the real webOS key ishdrCinemaBright(verified). The TV reportinghdrCinemaBrightleftmodeNumstale and writing that mode was rejected by validation - Replaced the key at the same position (nr 16) — existing numeric mappings unchanged
modeNumnow falls back to0for any unrecognised mode instead of keeping a stale value
- Fix: A weekend webOS system-app update (TV sw 33.31.68) changed alert timing — the single
closeAlertat 20 ms fired before the alert was registered and was ignored, leaving an empty "OK" dialog on screen that stacked with every verify-retry closeAlertnow fires repeatedly (40 / 150 / 400 / 900 ms) across the registration window- The previous alert is pre-closed before any retry, so dialogs can no longer stack
- Note: webOS system apps update independently of firmware, often on weekends — this class of regression recurs; see the technical notes
- Improvement: Faster connection pickup — fast-retry window polls every 3 s (was 5 s), regular backoff cap lowered to 15 s (was 30 s)
- Recommended companion: publish
lgtv/set/power = truefrom your home automation when it detects the TV turning on — the adapter opens the fast-retry window immediately
- Fix: With Loxone gating its commands on
info.connection, nothing external kicks the adapter anymore — a TV powered on after deep sleep waited up to 5 min for the next backoff slot - Backoff cap lowered from 300 s to 30 s (attempts are silent, so no log spam)
- A dropped established link opens a 2-minute window of 5 s polling — a TV switching state (standby wake) is caught within seconds
- Fix: A command-forced reconnect that hit a still-booting TV failed once and fell back to the backoff slot (up to 5 min), letting the queued scene expire — forced reconnects now open the 60 s fast-retry window (5 s polling), same as WoL
- Queued command max age raised from 120 s to 180 s
- Unknown
picture.mode/audio.soundModevalues are rejected with a warning before reaching the TV or the offline queue
- Fix: Reconnect loop could die permanently when a connect attempt got a socket that never fired
open/close— the stuckconnectingflag blocked all future attempts - 15 s watchdog per connect attempt: force-clean and reschedule if neither event fires
- Forced reconnects override attempts stuck longer than 15 s
disconnect()now also callsterminate()—close()is a no-op on sockets stuck in CONNECTING
- Fix: States set before the MQTT client finished connecting (typically
info.connectionwhen the TV link came up first) were silently dropped and never re-published - All cached states are re-published on every MQTT (re)connect
- Fix: Commands arriving milliseconds before the reconnect completed were lost forever (one-shot Loxone scenes fired shortly after TV-on detection)
- Commands received while disconnected are queued (latest value per key, max age 2 min) and replayed 2 s after connecting through the normal dedup + verify path
- Dedup caches are cleared on disconnect — stale values from the previous session no longer swallow commands while the TV is off
notifyandremote.*are never replayed late (time-sensitive)
- Fix: After a cold boot (night in deep standby) webOS silently drops or overrides early settings writes — the TV would ignore the morning scene until power-cycled
- Picture/sound writes are now verified against TV push confirmations and invisibly re-sent (up to 4 attempts at 2/4/8/15 s) within 2 minutes of connecting
- New
_confirmedstore tracks only TV-reported values (optimistic writes no longer mask verification)
- Fix: Incoming command while disconnected forces an immediate reconnect — a TV turned on in the morning connects within seconds instead of waiting for the next backoff slot (up to 5 min)
- WoL opens a 60 s fast-retry window (polling every 5 s) while the TV boots
- Ignored commands are removed from the 55 s throttle, so a re-send applies right after reconnecting
- Stale socket handlers are detached before a new connection — prevents duplicate reconnect loops
- Feature: New
notifystate — native toast notification on the TV screen (ssap://system.notifications/createToast) - Writable over MQTT at
lgtv/set/notify— e.g. from Loxone: doorbell, alarms, appliances - Bypasses command deduplication so repeated identical messages always display
- Two-layer command deduplication (TV state cache + 55 s throttle) against MQTT spam
closeAlertat 20 ms confirmed required on webOS 24 (isSysReq+timeout:0alone insufficient)- Exponential backoff reconnect (10 s → 300 s) — no log spam while the TV is off
- Fix: Critical bug in v1.2.28 —
createAlertrejected by webOS 24 with"Message can't be empty"whentitle/messagewere empty strings; restored single-space values - Fix: Removed all
ssap://input/sendButtoncalls (404 on webOS 24) - Simplified pointer socket: silent no-op on webOS 24 (404), works on older webOS as before
- Added fallback: if
createAlertfails for any reason, falls back to directssap://settings/setSystemSettings
- Add
CONTROL_MOUSE_AND_KEYBOARDandCONTROL_INPUT_TEXTto unsigned permissions - Improved pointer socket diagnostics: log actual error message instead of
{} _setWithAlert: addedisSysReq: true+modal: false— system-level alert auto-dismisses on webOS 24- (contained the empty title/message regression — superseded by 1.2.29)
- Added diagnostic logging for pointer socket failures (revealed 404 on webOS 24)
_setWithAlert: multiple ENTER retries inside createAlert callback
_setWithAlert: fallback from ENTER to BACK when modal is active
- Fix: move
setTimeout(pressEnter)inside createAlert callback — it was firing before TV responded
- Pointer socket text-frame format (
type:button\nname:ENTER\n\n) - Various timing and dismiss attempts for webOS 24 alert dialog
- Feature: Built-in MQTT client — configure broker host, port, credentials and topic prefix directly in adapter settings
- All states published automatically to
{prefix}/state/{name}withretain: true - Commands received on
{prefix}/set/{name}— supports strings, numbers and booleans - No separate ioBroker MQTT adapter required
- Fix: Use the real LG-signed RSA manifest from lgtv2 — TV now validates the signature and grants
WRITE_SETTINGS, enabling write operations for picture and sound settings via SSAP
- Experimental: Luna notification trick for settings writes (superseded by 1.1.6)
- Add
WRITE_SETTINGSto unsigned permissions array
- Add
CONTROL_DISPLAYto signed manifest permissions
- Fix: acknowledge all state changes (no more red values in ioBroker)
- Fix: sync numeric and string states on write
- Add numeric mirror states for picture mode, sound mode and sound output (for MQTT / Loxone)
- Full English translation of all code, comments, logs and object names
- Push subscriptions for picture and sound settings (instant updates)
- Polling fallback every 60 seconds
- Initial release: WebSocket/SSAP on port 3001 (TLS), custom LgTvSocket class, no lgtv2 dependency
- Picture Mode, Sound Mode, volume, mute, inputs, channels, remote control, Wake-on-LAN
MIT — © 2024 KPIotr89
Made with ❤️ for ioBroker and LG OLED owners
Tested on LG OLED77G4 · webOS 24 · js-controller 7.0
