Skip to main content

LG Horizon API Python Library

A Python library to interact with and control LG Horizon set-top boxes (Ziggo, Telenet, Virgin Media, UPC, BASE TV). Provides authentication, real-time device monitoring via MQTT, and remote control capabilities.

Supported Providers

Code Provider Country
nl Ziggo Netherlands
be-nl Telenet Belgium
be-basetv BASE TV Belgium
ch UPC Switzerland Switzerland
gb Virgin Media United Kingdom
ie Virgin Media Ireland
pl UPC Poland

Features

Authentication

  • Username/password and refresh token authentication
  • Automatic access token refreshing
  • Token refresh callback for persisting new tokens
  • Support for provider-specific auth flows

Device Management

  • Discover all set-top boxes on your account
  • Device info: manufacturer, model, platform type
  • Real-time availability monitoring (online/standby/offline)

Real-time Status via MQTT

  • Live device state changes via callback
  • Playback info: channel, show title, episode, season/episode numbers
  • Source types: linear TV, replay, VOD, nDVR, localDVR, review buffer, apps
  • Media types: channel, movie, episode, app
  • Playback position, duration, speed, paused state
  • Channel and program images
  • Automatic MQTT reconnection with exponential backoff

Channel Information

  • Full channel list with logos and stream images
  • Channel number, radio flag, linear products
  • Replay pre/post padding info
  • Profile-specific favorite channels

Recording Management

  • List all recordings (single, season, show)
  • Recording states: recorded, ongoing
  • Episode details for season/show recordings
  • Recording quota and usage percentage
  • Play recordings on a set-top box

Device Control

  • Power on/off
  • Play, pause, stop
  • Rewind, fast forward
  • Channel up/down and direct channel selection
  • Record current program
  • Set player position (seek)
  • Send any remote control key press
  • Display custom messages on the TV screen

Installation

pip install lghorizon

Requirements: Python 3.13+, aiohttp, paho-mqtt (2.1 or newer, below 3), backoff

Quick Start

Create a secrets.json file:

{
  "username": "your_username",
  "password": "your_password",
  "country": "nl",
  "timezone": "Europe/Amsterdam"
}

For providers with refresh token auth (Telenet, UPC CH, Virgin Media GB), use "refresh_token" instead of username/password.

Basic usage

import asyncio
import aiohttp
from lghorizon import LGHorizonApi, LGHorizonAuth

async def main():
    async with aiohttp.ClientSession() as session:
        auth = LGHorizonAuth(session, "nl", username="user", password="pass")
        api = LGHorizonApi(auth, profile_id=None)

        try:
            await api.initialize()
            devices = await api.get_devices()

            # Print all devices
            for device in devices.values():
                print(f"{device.device_friendly_name} ({device.manufacturer} {device.model})")
                print(f"  State: {device.device_state.state.value}")
                print(f"  Available: {device.is_available}")

            # Get channels
            channels = await api.get_profile_channels()
            for ch in channels.values():
                print(f"  {ch.channel_number} - {ch.title}")

            # Monitor state changes
            async def on_state_change(device_id: str):
                device = devices[device_id]
                s = device.device_state
                print(f"{device.device_friendly_name}: {s.channel_name} - {s.show_title}")
                print(f"  Source: {s.source_type.value}, Position: {s.position}/{s.duration}")

            for device in devices.values():
                await device.set_callback(on_state_change)

            # Keep running to receive MQTT updates
            await asyncio.Event().wait()

        finally:
            await api.disconnect()

asyncio.run(main())

Device control

device = devices["device-id"]

# Power
await device.turn_on()
await device.turn_off()

# Playback
await device.play()
await device.pause()
await device.stop()
await device.rewind()
await device.fast_forward()

# Channels
await device.next_channel()
await device.previous_channel()
await device.set_channel("NPO 1")

# Recording
await device.record()
await device.play_recording("recording-id")

# Position (milliseconds)
await device.set_player_position(60000)

# Display message on screen
await device.display_message("linear", "Hello from Python!")

Recordings & quota

if api.has_cloud_recording:
    # Quota
    quota = await api.get_recording_quota()
    print(f"Used: {quota.occupied}/{quota.quota} MB ({quota.percentage_used:.1f}%)")

    # All recordings
    recordings = await api.get_all_recordings()
    for rec in recordings.recordings:
        print(f"[{rec.type.value}] {rec.title} ({rec.recording_state.value})")

    # Episodes of a show recording
    episodes = await api.get_show_recording_episodes("show-recording-id")
    for ep in episodes.recordings:
        print(f"  S{ep.season_number}E{ep.episode_number}: {ep.episode_title}")

Token refresh callback

async def on_token_refresh(new_token: str):
    # Persist the new refresh token for next session
    save_to_storage(new_token)

await api.set_token_refresh_callback(on_token_refresh)

Device State Properties

When monitoring a device, device.device_state exposes:

Property Type Description
state LGHorizonRunningState ONLINE_RUNNING, ONLINE_STANDBY, OFFLINE, etc.
ui_state_type LGHorizonUIStateType MAINUI, APPS, UNKNOWN
source_type LGHorizonSourceType LINEAR, VOD, NDVR, LOCALDVR, REPLAY, REVIEWBUFFER
media_type LGHorizonMediaType CHANNEL, MOVIE, EPISODE, APP
channel_id str | None Current channel ID
channel_name str | None Current channel name
show_title str | None Current show/movie/app title
episode_title str | None Current episode title
season_number int | None Season number
episode_number int | None Episode number
position int | None Playback position in seconds
duration int | None Content duration in seconds
start_time int | None Program start (Unix timestamp)
end_time int | None Program end (Unix timestamp)
speed int | None Playback speed (0 = paused, 1 = normal)
paused bool Whether playback is paused
image str | None Content/channel image URL
app_name str | None Active app name (when source is APPS)

Error Handling

from lghorizon import (
    LGHorizonApiError,              # Base for connection errors
    LGHorizonApiConnectionError,    # Network/connection issues
    LGHorizonApiUnauthorizedError,  # Invalid credentials or token (not a LGHorizonApiError)
    LGHorizonApiLockedError,        # Subclass of the unauthorized error
)

try:
    await api.initialize()
except LGHorizonApiUnauthorizedError:
    print("Invalid credentials or token")
except LGHorizonApiConnectionError:
    print("Could not connect to the API")
except LGHorizonApiError as e:
    print(f"API error: {e}")

LGHorizonApiUnauthorizedError is raised when the provider reports invalid credentials or an invalid token. Other failed calls raise LGHorizonApiConnectionError. It does not derive from LGHorizonApiError, so catch it separately. LGHorizonApiLockedError exists for locked accounts, but the library does not raise it yet.

Development

Setup

git clone https://github.com/Sholofly/lghorizon-python.git
cd lghorizon-python
pip install -e ".[test,lint]"

The version is taken from the git tags (setuptools_scm), so keep the tags when cloning.

Running tests

python -m pytest tests/ -v

Linting

ruff check .

CI runs the same lint check, the tests and a package build on every pull request.

Running the demo script

  1. Create a secrets.json (see Quick Start)
  2. Run python main.py

The demo script prints all profiles, devices, channels, recordings, and then monitors live state changes with a visual progress bar. It also provides an interactive command prompt to control your set-top boxes (send messages, switch channels, etc.). Type help for a list of available commands.

Web-based Test UI

A browser-based interface for testing all library features interactively:

python web.py

Then open http://localhost:8080 in your browser.

Features:

  • Login with your provider credentials (username/password or refresh token, depending on provider)
  • Live dashboard showing all set-top boxes with real-time state updates via WebSocket
  • Full device control: power, playback, channel switching, message display, raw key input
  • Event log with timestamped state changes
  • Optional "Remember credentials" to save your provider and username between sessions (passwords are never stored)

Note: No additional dependencies are required — web.py uses the same aiohttp that the library already depends on.

License

MIT License

Metadata

Release files for lghorizon 0.12.5

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for lghorizon 0.12.5
File Size Uploaded Provenance Actions
lghorizon-0.12.5.tar.gz 94.9 kB With provenance Details for lghorizon-0.12.5.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for lghorizon 0.12.5
File Interpreter ABI Platform Actions
lghorizon-0.12.5-py3-none-any.whl Python 3 none any Details for lghorizon-0.12.5-py3-none-any.whl

Total release size: 134.8 kB

Release files / lghorizon-0.12.5.tar.gz

Download URL lghorizon-0.12.5.tar.gz
Size 94.9 kB
Tags Source
SHA-256 checksum
How to use checksums
4f0fe321496423d4c5172eeadf6c85c9a8adb91fb70754bb778c40d05d08adb5
BLAKE2b-256 checksum
How to use checksums
0ceca1a7d57e3fbb4e6b302c718a08e0c7f779b070607e537e8fe510d7e8b26b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 30, 2026.

Transparency log

Release files / lghorizon-0.12.5-py3-none-any.whl

Download URL lghorizon-0.12.5-py3-none-any.whl
Size 40.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
4841de3abe0bec40e6ef20e0553208fc0372239e04765b655d6f45eee19a0235
BLAKE2b-256 checksum
How to use checksums
a333940db3f3c241bec648363192e0b096fe0a9abc7ec4c5ddb93b08ecbf5684
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 30, 2026.

Transparency log

Security for lghorizon 0.12.5

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

Both files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for lghorizon-0.12.5.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 30, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.12.5 This release

2 release files Full provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page