Skip to content

Replace Python TV bridge with native webOS client #20

@Staphylococcus

Description

@Staphylococcus

Problem statement

LG Buddy currently controls the TV through the Python bscpylgtvcommand CLI. The Rust runtime shells out to that command through BscpylgtvCommandClient, and the installer creates a Python virtual environment under the install prefix so bscpylgtv is available at runtime.

This has become a poor fit for the direction of the project:

  • it makes the install payload larger and more fragile
  • it requires python3-venv, python3-pip, and network/package-manager behavior during install unless the release bundle skips it
  • it complicates immutable distro support because the Python venv must be placed and wired correctly outside /usr
  • it forces TV errors through subprocess stdout/stderr parsing instead of structured runtime errors
  • it keeps auth/key ownership tied to bscpylgtv's .aiopylgtv.sqlite storage

The target model is a self-contained Rust runtime that speaks the LG webOS TV API directly.

This should be a replacement, not a long-lived dual backend. Avoid adding a permanent runtime choice between Python and native TV control.

Current Rust boundary

The existing shape is favorable for a clean replacement:

  • tv.rs already defines a narrow TvClient trait
  • screen and lifecycle policy use TvDevice / TvClient rather than directly invoking Python
  • most Python-specific code is contained in BscpylgtvCommandClient, subprocess launchers, and auth-owner handling

Target behavior

Implement a native WebOsTvClient that satisfies the existing LG Buddy TvClient contract:

  • query current input
  • set configured HDMI input
  • query OLED/backlight brightness
  • set OLED/backlight brightness
  • power off
  • turn screen off
  • turn screen on

Expected webOS surfaces, based on the current bscpylgtv behavior:

LG Buddy operation webOS/SSAP surface
current input com.webos.applicationManager/getForegroundAppInfo
set HDMI input tv/switchInput with inputId
power state guard com.webos.service.tvpower/power/getPowerState
power off system/turnOff
screen off com.webos.service.tvpower/power/turnOffScreen with standbyMode: active
screen on com.webos.service.tvpower/power/turnOnScreen with standbyMode: active
get picture settings settings/getSystemSettings for category picture
set picture settings likely needs the same Luna/settings path currently used by bscpylgtv

Screen on/off may need endpoint fallback for older webOS versions:

  • com.webos.service.tvpower/power/turnOffScreen
  • com.webos.service.tvpower/power/turnOnScreen
  • older fallback: com.webos.service.tv.power/turnOffScreen
  • older fallback: com.webos.service.tv.power/turnOnScreen

Auth and pairing

Native auth should be owned by LG Buddy, not by bscpylgtv.

Preliminary target:

~/.config/lg-buddy/webos-client-key.json

Requirements:

  • create/update the native key file as the config owner, not as root
  • use file permissions appropriate for local credential material, e.g. 0600
  • pair through the normal TV prompt when no key exists
  • preserve clear error output when pairing is required or rejected

A one-time migration helper from .aiopylgtv.sqlite is acceptable if it is simple and well-tested, but it should not become a permanent runtime fallback. If migration is not reliable enough, requiring one re-pair during the native transition is acceptable.

Implementation direction

  1. Add native webOS transport primitives:
    • websocket connection to wss://<tv-ip>:3001
    • TLS verification disabled as bscpylgtv currently does
    • registration payload with the required manifest and existing/new client key
    • request IDs and response correlation
    • request timeout and connection retry behavior
  2. Add structured native TV errors:
    • connection failure
    • pairing required/rejected
    • response timeout
    • webOS error response with preserved error code/message
    • unsupported endpoint
  3. Implement WebOsTvClient for the existing TvClient trait.
  4. Replace runtime construction sites so LG Buddy uses the native client directly.
  5. Remove Python/subprocess-specific TV runtime code:
    • BscpylgtvCommandClient
    • command launcher types
    • Python helper env vars, except any short-lived migration-only handling
    • stderr parsing for known webOS codes where native structured errors can replace it
  6. Update installer/release code:
    • stop checking for python3-venv and python3-pip
    • stop creating LG_Buddy_PIP
    • stop installing bscpylgtv
    • remove LG_BUDDY_BSCPYLGTV_COMMAND wiring
  7. Update docs and architecture overview to describe native webOS TV control.

Testing scope

Unit/integration tests should use a mock webOS websocket server rather than a real TV.

Coverage should include:

  • connect and register with an existing client key
  • connect and register through a new pairing response
  • rejected/failed pairing
  • request/response correlation by ID
  • malformed response handling
  • webOS error response mapping, including preserving codes such as -102
  • current-input query
  • set-input request payload
  • power-off guard behavior using power state
  • screen off/on request payloads and older endpoint fallback
  • brightness get/set payloads and parsing
  • auth key file ownership/path/permission behavior where practical

Release smoke coverage should assert that the installed bundle no longer creates a Python virtual environment or requires bscpylgtvcommand.

Manual validation should cover at least:

  • first-time pairing on a real LG webOS TV
  • existing paired TV after restart/reboot
  • screen off/on from idle and lock-screen activity
  • suspend pre-sleep power-off and resume restore
  • brightness command
  • behavior when the TV is off, active standby, or unreachable

Dependency relationship

This is a prerequisite for first-class immutable distro support. Tracked in #24.

References

  • Current Python bridge: crates/lg-buddy/src/tv.rs
  • Current auth-owner helper: crates/lg-buddy/src/auth.rs
  • Current installer Python venv setup: install.sh

Migrated from #26 before repository ownership transfer.
Original issue: #26
Original author: @Staphylococcus
Original created: 2026-04-30T17:26:57Z
Original comments at migration time: none.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions