Skip to main content

Python client for Liberty Global Horizon settop boxes

Project description

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.10+, aiohttp, paho-mqtt, 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 exception
    LGHorizonApiConnectionError,    # Network/connection issues
    LGHorizonApiUnauthorizedError,  # Invalid credentials
    LGHorizonApiLockedError,        # Account locked
)

try:
    await api.initialize()
except LGHorizonApiLockedError:
    print("Account is locked, try again later")
except LGHorizonApiUnauthorizedError:
    print("Invalid credentials")
except LGHorizonApiConnectionError:
    print("Could not connect to the API")
except LGHorizonApiError as e:
    print(f"API error: {e}")

Development

Setup

git clone https://github.com/Sholofly/lghorizon-python.git
cd lghorizon-python
pip install -e .
pip install pytest pytest-asyncio

Running tests

python -m pytest tests/ -v

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.

License

MIT License

Project details


Release history Release notifications | RSS feed

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

lghorizon-0.10.7.tar.gz (61.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

lghorizon-0.10.7-py3-none-any.whl (32.7 kB view details)

Uploaded Python 3

File details

Details for the file lghorizon-0.10.7.tar.gz.

File metadata

  • Download URL: lghorizon-0.10.7.tar.gz
  • Upload date:
  • Size: 61.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for lghorizon-0.10.7.tar.gz
Algorithm Hash digest
SHA256 f1992440452a4849d4a6ba560137b649a2b1fd003f2c51bbbe91cd63d85ec719
MD5 f400615bc19f08267089598721b0c105
BLAKE2b-256 4e420241ae2ae3beebff3bac93d0cb09ce4bc7a56086906871df4237f28d4060

See more details on using hashes here.

Provenance

The following attestation bundles were made for lghorizon-0.10.7.tar.gz:

Publisher: publish-to-pypi.yml on Sholofly/lghorizon-python

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file lghorizon-0.10.7-py3-none-any.whl.

File metadata

  • Download URL: lghorizon-0.10.7-py3-none-any.whl
  • Upload date:
  • Size: 32.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for lghorizon-0.10.7-py3-none-any.whl
Algorithm Hash digest
SHA256 6bd5992998dde40a8a94b0cd7ecd6041d9ef85bbffa83190edad69a7974508ed
MD5 086f7883d8551c03760372b386e3d2ef
BLAKE2b-256 40f02a57f6fca293f02c8387614ea2b08bedc2ef95d7a58a046f81e47163820f

See more details on using hashes here.

Provenance

The following attestation bundles were made for lghorizon-0.10.7-py3-none-any.whl:

Publisher: publish-to-pypi.yml on Sholofly/lghorizon-python

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page