Small macOS menu bar app and CLI for controlling an LG webOS TV.
The app talks to the TV directly from Swift over the webOS WebSocket API and the LG IP-control TCP protocol. Pairing credentials (client key, IP control keycode) live in macOS Keychain under the com.lgtv-control service; non-secret config (host, MAC addresses) stays in a JSON file at ~/.config/lgtv-pairing.json.
brew install --cask li-yifei/tap/lgtv-controlThis installs the menu bar app to /Applications and symlinks a lgtv CLI into Homebrew's bin directory. The app is self-signed, so the cask strips the quarantine attribute on install.
To uninstall:
brew uninstall --cask lgtv-control- Launch LG TV Control from Spotlight or Launchpad.
- From the menu bar icon → Pair / Re-pair, enter the TV's IP and accept the on-screen prompt on the TV. The client key is saved to Keychain.
- (Optional, only needed for service-menu PIN entry) On the TV, navigate to Settings → All Settings → Network → LG Connect Apps and copy the 8-character IP control keycode. In the menu bar app open Settings → IP Control Keycode, paste it, and save.
Use v0.5.1 or later with webOS 26. Some newer firmware rejects the legacy registration certificate with:
403 Pairing rejected: blacklisted certificate detected
The app first sends the original signed registration handshake for older TVs. When the TV explicitly reports this certificate error, it reconnects and retries once with an unsigned manifest, preserving the requested permissions. Other errors are returned normally. This applies to both the menu bar app and CLI.
After upgrading the TV firmware:
- Install v0.5.1 or later and restart the app.
- Try Refresh to reconnect with the saved pairing key.
- If authorization is needed, select Pair / Re-pair, enter the TV's current IP address, and accept the prompt on the TV.
Validation covers live status retrieval on webOS 26 and automated regression checks for the legacy manifest, permission preservation, and bounded fallback. Older firmware compatibility has been checked at the code level; coverage across all TV models and firmware versions remains incomplete. Service-menu access and IP-control PIN entry require separate firmware-specific verification.
- Volume up / down, mute toggle, set volume by slider
- Safety volume reminder with configurable threshold
- Power on (Wake-on-LAN with auto-discovered MACs) and power off
- HDMI input list and switching
- Extra → InStart / EZ Adjust: launch LG service menus and auto-type the 4-digit PIN over IP control
- App Intents for Shortcuts and Siri (power, volume, mute, input switching)
- Customizable single-letter menu shortcuts
- Structured CLI (
lgtv) with--jsonfor scripts and Shortcuts shell actions
lgtv --help
lgtv status --json
lgtv volume set 12
lgtv volume up --steps 3
lgtv mute toggle
lgtv power on
lgtv input list
lgtv input switch HDMI_2
lgtv raw ssap://audio/getVolume --jsonReads LG_TV_CONFIG first, then ~/.config/lgtv-pairing.json. Individual commands can override with --config PATH. Data commands write JSON to stdout with --json; failures go to stderr with non-zero exit.
./build.shProduces build/LG TV Control.app, build/bin/lgtv, and build/LG-TV-Control.app.zip (release artifact).
Run the registration compatibility checks locally (no TV or pairing credentials required):
sh scripts/test-registration.shFor stable Keychain access across rebuilds, generate a local self-signed code signing cert once:
./scripts/setup-codesign.shRequires openssl@3 (brew install openssl@3). Without the cert, build.sh falls back to ad-hoc signing, and macOS will re-prompt for Keychain authorization on every rebuild.
MIT. Bundled third-party software (Apple's swift-argument-parser, Apache 2.0) is acknowledged in THIRD_PARTY_LICENSES.md.