Summary
IP control specification for 2018-model LG webOS 4.0 televisions. Commands are ASCII text terminated with carriage return (\r, 0x0d), encrypted with AES-128-CBC over TCP port 9761. The AES key is derived from an 8-character alphanumeric password (shown on the TV's IP Control Setup menu) via PBKDF2-SHA256 with a fixed 16-byte salt and 2^14 iterations. The IV is randomly generated per command and prepended to the encrypted payload, itself encrypted using AES-128-ECB before transmission. Discovery is supported via SDDP (Control4) and SSDP (UPnP). Power-on uses Wake-on-LAN magic packets rather than an in-band command.
Transport
protocols:
- tcp
addressing:
port: 9761
auth:
type: pre_shared_key # 8-digit alphanumeric password shown on TV IP Control Setting menu; AES-128 key derived via PBKDF2-SHA256 (salt 0x63,0x61,0xb8,0x0e,0x9b,0xdc,0xa6,0x63,0x8d,0x07,0x20,0xf2,0xcc,0x56,0x8f,0xb9; iterations 2^14; first 16 bytes of derived key used)
encryption:
algorithm: AES-128-CBC
block_length_bits: 128
key_length_bits: 128
iv_length_bits: 128
iv_handling: per_message_random_iv_prefixed_and_encrypted_with_AES_128_ECB
payload_terminator: "\\r" # 0x0d
padding: pkcs7_style # source: "value to be padded is the number of bytes to be padded"
discovery:
- protocol: SDDP
version: "1.0"
events: [NOTIFY ALIVE, NOTIFY IDENTIFY, NOTIFY OFFLINE]
methods: [SEARCH]
- protocol: SSDP
device_types:
- urn:schemas-upnp.org:device:Basic:1
- urn:schemas-upnp.org:service:dial:1
- urn:schemas-upnp.org:device:MediaRenderer:1
events: [ssdp:alive, ssdp:byebye]
wake_on_lan:
supported: true
magic_packet_length_bytes: 102
default_port: 4343 # source: "Port number 4343 or any number can be used"
notes: "WoWLAN requires router with WME/WMM; wired Ethernet must be disconnected when testing WoWLAN."
Traits
- powerable # inferred from POWER off command and Wake-on-LAN documentation
- queryable # inferred from GET_* and CURRENT_* query commands
- routable # inferred from INPUT_SELECT command
- levelable # inferred from VOLUME_CONTROL, PICTURE_BACKLIGHT, PICTURE_CONTRAST, etc.
- mutable # inferred from VOLUME_MUTE and SCREEN_MUTE commands
Actions
- id: power_off
label: Power Off
kind: action
command: "POWER off\r"
params: []
notes: "Source documents only 'POWER off'. Power-on is performed via Wake-on-LAN magic packet (see Transport.wake_on_lan)."
- id: aspect_ratio_set
label: Set Aspect Ratio
kind: action
command: "ASPECT_RATIO {mode}\r"
params:
- name: mode
type: enum
values: [4by3, 16by9, setbyoriginal]
- id: screen_mute_set
label: Set Screen Mute
kind: action
command: "SCREEN_MUTE {mode}\r"
params:
- name: mode
type: enum
values: [screenmuteon, videomuteon, allmuteoff]
description: "screenmuteon mutes OSD and video; videomuteon mutes video only; allmuteoff disables all mutes."
- id: volume_mute_set
label: Set Volume Mute
kind: action
command: "VOLUME_MUTE {state}\r"
params:
- name: state
type: enum
values: [on, off]
- id: volume_control_set
label: Set Volume Level
kind: action
command: "VOLUME_CONTROL {level}\r"
params:
- name: level
type: integer
min: 0
max: 100
- id: picture_mode_set
label: Set Picture Mode
kind: action
command: "PICTURE_MODE {mode}\r"
params:
- name: mode
type: enum
values: [vivid, eco, normal, game, cinema, sports]
description: "Eco maps to APS; Normal maps to Standard."
- id: picture_backlight_set
label: Set Picture Backlight
kind: action
command: "PICTURE_BACKLIGHT {level}\r"
params:
- name: level
type: integer
min: 0
max: 100
- id: picture_contrast_set
label: Set Picture Contrast
kind: action
command: "PICTURE_CONTRAST {level}\r"
params:
- name: level
type: integer
min: 0
max: 100
- id: picture_brightness_set
label: Set Picture Brightness
kind: action
command: "PICTURE_BRIGHTNESS {level}\r"
params:
- name: level
type: integer
min: 0
max: 100
- id: picture_colour_set
label: Set Picture Colour
kind: action
command: "PICTURE_COLOUR {level}\r"
params:
- name: level
type: integer
min: 0
max: 100
- id: picture_tint_set
label: Set Picture Tint
kind: action
command: "PICTURE_TINT {level}\r"
params:
- name: level
type: integer
min: 0
max: 100
description: "0 maps to R50, 50 maps to 0, 100 maps to G50."
- id: picture_sharpness_set
label: Set Picture Sharpness
kind: action
command: "PICTURE_SHARPNESS {level}\r"
params:
- name: level
type: integer
min: 0
max: 50
- id: picture_colour_temperature_set
label: Set Picture Colour Temperature
kind: action
command: "PICTURE_COLOUR_TEMPERATURE {level}\r"
params:
- name: level
type: integer
min: 0
max: 100
description: "0 maps to W50, 50 maps to 0, 100 maps to C50."
- id: remote_controller_lock_set
label: Set Remote Controller Lock
kind: action
command: "REMOTECONTROLER_LOCK {state}\r"
params:
- name: state
type: enum
values: [on, off]
- id: audio_balance_set
label: Set Audio Balance
kind: action
command: "AUDIO_BALANCE {level}\r"
params:
- name: level
type: integer
min: 0
max: 100
description: "0 maps to L50, 50 maps to 0, 100 maps to R50."
- id: audio_equalizer_set
label: Set Audio Equalizer Band
kind: action
command: "AUDIO_EQUALIZER {band} {value}\r"
params:
- name: band
type: integer
min: 1
max: 5
description: "1=100Hz, 2=300Hz, 3=1kHz, 4=3kHz, 5=10kHz"
- name: value
type: integer
min: 0
max: 20
description: "Value range 0..20 maps to -10..+10 dB (0 -> -10, 20 -> +10)."
- id: energy_saving_set
label: Set Energy Saving Mode
kind: action
command: "ENERGY_SAVING {mode}\r"
params:
- name: mode
type: enum
values: [auto, screenoff, Maximum, medium, minimum, off]
- id: channel_setting_atsc_atv
label: Tune ATSC Analog Channel
kind: action
command: "CHANNEL_SETTING_ATSC_ATV {channel} {source}\r"
params:
- name: channel
type: integer
description: "Analog channel number"
- name: source
type: enum
values: [antenna, cable]
- id: channel_setting_atsc_dtv_single
label: Tune ATSC Digital Channel (cable major-only)
kind: action
command: "CHANNEL_SETTING_ATSC_DTV {channel} cablemaj\r"
params:
- name: channel
type: integer
description: "Major channel number for cable cablemaj tuning"
- id: channel_setting_atsc_dtv_antenna
label: Tune ATSC Digital Channel (antenna, major/minor)
kind: action
command: "CHANNEL_SETTING_ATSC_DTV {major} {minor} antennanotphy\r"
params:
- name: major
type: integer
description: "Major channel number"
- name: minor
type: integer
description: "Minor channel number"
- id: channel_setting_atsc_dtv_cable
label: Tune ATSC Digital Channel (cable, major/minor)
kind: action
command: "CHANNEL_SETTING_ATSC_DTV {major} {minor} cablenotphy\r"
params:
- name: major
type: integer
description: "Major channel number"
- name: minor
type: integer
description: "Minor channel number"
- id: channel_add_delete
label: Add or Delete Channel
kind: action
command: "CHANNEL_ADD_DELETE {operation}\r"
params:
- name: operation
type: enum
values: [add, delete]
- id: key_action
label: Send Remote Key
kind: action
command: "KEY_ACTION {key}\r"
params:
- name: key
type: enum
values:
- exit
- channelup
- channeldown
- volumeup
- Volumedown
- arrowright
- arrowleft
- volumemute
- deviceinput
- sleepreserve
- livetv
- previouschannel
- favoritechannel
- teletext
- teletextoption
- returnback
- avmode
- captionsubtitle
- arrowup
- arrowdown
- myapp
- settingmenu
- ok
- quickmenu
- videomode
- audiomode
- channellist
- bluebutton
- yellowbutton
- greenbutton
- redbutton
- aspectratio
- audiodescription
- programmorder
- userguide
- smarthome
- simplelink
- fastforward
- rewind
- programminfo
- programguide
- play
- slowplay
- soccerscreen
- reord
- 3d
- autoconfig
- app
- screenbright
- number0
- number1
- number2
- number3
- number4
- number5
- number6
- number7
- number8
- number9
notes: "Source labels KEY_ACTION as Toggle; individual key tokens are discrete payloads."
- id: osd_select_set
label: Set OSD Display
kind: action
command: "OSD_SELECT {state}\r"
params:
- name: state
type: enum
values: [on, off]
- id: input_select
label: Select Input Source
kind: action
command: "INPUT_SELECT {input}\r"
params:
- name: input
type: enum
values: [dtv, atv, cadtv, catv, avav1, component1, hdmi1, hdmi2, hdmi3]
notes: "Source recommends LAUNCH_APP as preferred method for HDMI input selection."
- id: picture_3d_toggle
label: Toggle 3D Picture
kind: action
command: "PICTURE_3D\r"
params: []
notes: "3D models only. Source marks this as Toggle."
- id: picture_3d_extension_toggle
label: Toggle Extended 3D Picture
kind: action
command: "PICTURE_3D_EXTENSION\r"
params: []
notes: "3D models only. Source marks this as Toggle."
- id: launch_app
label: Launch Application
kind: action
command: "LAUNCH_APP {appid}\r"
params:
- name: appid
type: string
description: "Application identifier (see Notes for sample IDs)."
- id: get_macaddress
label: Get MAC Address
kind: query
command: "GET_MACADDRESS {interface}\r"
params:
- name: interface
type: enum
values: [wired, wifi]
response_format: "MAC address in MM:MM:MM:SS:SS:SS format"
notes: "Section 3.5 lists token as 'wire|wifi'; section 7.2 lists 'wired|wifi'. Both forms appear in source - verify against device."
- id: mute_state_query
label: Query Mute State
kind: query
command: "MUTE_STATE\r"
params: []
response_format: "MUTE:on or MUTE:off"
- id: current_vol_query
label: Query Current Volume
kind: query
command: "CURRENT_VOL\r"
params: []
response_format: "VOL:<numerical value>"
- id: current_app_query
label: Query Current Application
kind: query
command: "CURRENT_APP\r"
params: []
response_format: "APP:<application id>"
- id: current_ch_query
label: Query Current Channel
kind: query
command: "CURRENT_CH\r"
params: []
response_format: "CH:<Channel Name> <Major>-<Minor>"
notes: "Returns UNKNOWN for channel name if unavailable. Returns NONE if a non-LiveTV application is active."
- id: get_ipcontrol_state
label: Query IP Control Service State
kind: query
command: "GET_IPCONTROL_STATE\r"
params: []
response_format: "ON if server is healthy; otherwise the command times out."
Feedbacks
- id: mute_state
type: enum
values: [on, off]
source_query: mute_state_query
response_pattern: "MUTE:{value}"
- id: volume_level
type: integer
min: 0
max: 100
source_query: current_vol_query
response_pattern: "VOL:{value}"
- id: current_application
type: string
source_query: current_app_query
response_pattern: "APP:{appid}"
- id: current_channel
type: object
fields:
- name: name
type: string
description: "Channel name; 'UNKNOWN' if unavailable; 'NONE' if non-LiveTV app is active."
- name: major
type: integer
- name: minor
type: integer
source_query: current_ch_query
response_pattern: "CH:{name} {major}-{minor}"
- id: mac_address
type: string
source_query: get_macaddress
response_pattern: "MM:MM:MM:SS:SS:SS"
- id: ip_control_state
type: enum
values: [ON]
source_query: get_ipcontrol_state
notes: "Only 'ON' is documented as a response. Unhealthy state is observed as a TCP timeout, not a payload."
- id: generic_command_ack
type: string
response_pattern: "OK\\n"
notes: "Source shows VOLUME_MUTE on returning 'OK\\n' (with subsequent bytes padded with 0x7d). UNRESOLVED: whether all non-query commands share this acknowledgement format."
Variables
# UNRESOLVED: settable parameters distinct from actions are not separately enumerated in the source.
# The level/enum parameters of the *_set actions above cover the configurable surface.
Events
- id: ssdp_alive
source: SSDP
message: "ssdp:alive"
notes: "Emitted per device/service type at TV power-up."
- id: ssdp_byebye
source: SSDP
message: "ssdp:byebye"
notes: "Emitted per device/service type at TV power-down."
- id: sddp_alive
source: SDDP
message: "NOTIFY ALIVE"
notes: "Emitted on TV startup."
- id: sddp_identify
source: SDDP
message: "NOTIFY IDENTIFY"
notes: "Emitted when Control4 SDDP is enabled on IP Control Setup screen."
- id: sddp_offline
source: SDDP
message: "NOTIFY OFFLINE"
notes: "Emitted before TV power-off."
Macros
- id: power_on_via_wol
label: Power On (via Wake-on-LAN)
steps:
- description: "Send WoL magic packet (6 bytes of 0xFF followed by 16 repetitions of the TV's 48-bit MAC address; 102 bytes total) to the TV's IP or subnet broadcast address on UDP port 4343 (or any port)."
notes: "Requires both controller and TV on the same subnet. For WoWLAN, the router must support WME/WMM, and the wired Ethernet cable must be disconnected during testing. Settings -> General -> Mobile TV On -> 'Turn on via Wi-Fi' must be on for WoWLAN."
- id: ip_control_setup
label: Open IP Control Configuration Menu (manual)
steps:
- description: "On the TV, open Settings menu and focus the Network icon."
- description: "Press the numerical key sequence 828888 on the IR remote to display the IP Control Configuration menu."
notes: "Required once per TV to retrieve the 8-character alphanumeric password used for AES key derivation."
Safety
confirmation_required_for: []
interlocks: []
# UNRESOLVED: source contains no explicit safety warnings, interlock procedures, or power-on sequencing requirements beyond the WoL/WoWLAN prerequisites documented under Macros.
Notes
Sample application IDs (webOS 4.0)
Third-party:
- Amazon →
amazon - Google Play →
googleplaymovieswebos - Hulu →
com.hulu.hulu - Netflix →
netflix - SlingTV →
com.movenetworks.app.sling-tv-sling-production - Youtube →
youtube.leanback.v4 - Vudu →
vudu
System built-in:
- Settings →
com.palm.app.settings - Photo & Video →
com.webos.app.photovideo - Music →
com.webos.app.music - Guide →
com.webos.service.iepg - Browser →
com.webos.app.browser
Encryption worked example (from source)
Plaintext: VOLUME_MUTE on\r (15 bytes). Padded to 16 bytes by appending 0x01:
56 4f 4c 55 4d 45 5f 4d 55 54 45 20 6f 6e 0d 01
With hard-coded developmental password 12345678 (PBKDF2-derived key
9B B9 91 16 DD C1 33 E0 B0 5B 76 D8 BA 71 3A B0) and zero IV
(00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00), the AES-128-ECB-encrypted IV is:
D2 B2 1C A0 AD 64 86 CB 20 56 A8 B8 15 03 35 08
The AES-128-CBC-encrypted payload (using the raw IV before ECB-encryption):
d2 d3 2b fe ac 13 f4 43 5e 6f 2f 54 2e ea 9c 18
On-wire transmission: [ECB-encrypted IV (16 bytes)] || [CBC-encrypted payload (16 bytes)].
Response from device for the VOLUME_MUTE on command (after CBC decryption with the received IV):
4f 4b 0a 00 7d 7d 7d 7d 7d 7d 7d 7d 7d 7d 7d 7d → OK\n (bytes after \n are padding and should be ignored).
Command framing reminders
- All command strings terminate with
\r(0x0d) before padding. - If the (terminated) plaintext is not a multiple of 16 bytes, pad with
Nrepeated bytes of valueN(whereNis the number of pad bytes needed). - A fresh random IV must be generated per command; the IV is sent ECB-encrypted, prefixed to the CBC-encrypted payload.
Provenance
source_domains:
- raw.githubusercontent.com
source_urls:
- https://raw.githubusercontent.com/WesSouza/lgtv-ip-control/main/docs/LG_IP.pdf
retrieved_at: 2026-04-30T04:31:17.466Z
last_checked_at: 2026-06-02T17:22:51.416Z
Verification Summary
verdict: verified
checked_at: 2026-06-02T17:22:51.416Z
matched_actions: 34
action_count: 34
confidence: medium
summary: "All 34 spec actions matched verbatim in source; all transport parameters verified; source command set fully represented. (11 unresolved item(s) noted in Known Gaps.)"
Known Gaps
- "firmware version compatibility ranges not stated"
- "response/acknowledgement format for non-query commands not fully documented beyond the \"OK\\n\" example for VOLUME_MUTE"
- "whether all non-query commands share this acknowledgement format.\""
- "settable parameters distinct from actions are not separately enumerated in the source."
- "source contains no explicit safety warnings, interlock procedures, or power-on sequencing requirements beyond the WoL/WoWLAN prerequisites documented under Macros."
- "response format / ack pattern for non-query commands beyond the single VOLUME_MUTE example."
- "behavior when a query (other than GET_IPCONTROL_STATE) is issued while the TV is powered off or in standby."
- "maximum command rate, inter-command delay requirements, and TCP keep-alive expectations."
- "whether multiple concurrent TCP clients on port 9761 are supported."
- "exact firmware versions supported (source covers \"2018 LG TV\" / webOS 4.0 generally without a version matrix)."
- "GET_MACADDRESS interface token spelling discrepancy ('wire' in §3.5 vs 'wired' in §7.2)."
From the AI4AV catalog (https://ai4av.net) · ODbL-1.0