# Liberty Global Horizon Binding

The Liberty Global Horizon binding integrates set-top boxes running the Liberty Global Horizon platform.

Provider Country Auth
BASE TV Belgium refresh token
Telenet TV Belgium refresh token
UPC / Sunrise Switzerland refresh token
Virgin Media Ireland username/password
Virgin Media UK refresh token
Ziggo Netherlands username/password
UPC Poland username/password

Status and control both go over the same cloud MQTT channel the official apps use. There is no local API on the box itself.

# Supported Things

Thing Type Description
account Bridge One LG Horizon account/household. Owns the login session and the shared MQTT connection.
box Thing One physical set-top box. Discovered automatically under an account bridge.

# Discovery

Once an account is configured and active, the binding will discover available set-top boxes.

# Thing Configuration

# account (Bridge)

Parameter Required Advanced Description
provider no no Provider: telenet, basetv, ziggo, upc-sunrise, virginmedia-gb, virginmedia-ie, upc-poland. Leave empty ("Custom") to configure an unlisted provider manually with the three advanced fields below.
country no yes Only used when provider is empty: two-letter locale used in the service-discovery URL path (e.g. be) - not necessarily the same region as the API URL.
apiUrl no yes Only used when provider is empty: base URL of the provider's "spark" REST API.
useRefreshToken no yes Only used when provider is empty: whether the provider needs refresh-token auth instead of username/password. Default false.
username no no Only for password-based providers
password no no Only for password-based providers
refreshToken no no Only for refresh-token-based providers

# Refresh token based authentication (BASE TV, Telenet, UPC/Sunrise, Virgin Media GB)

These providers don't expose a public login endpoint for third-party apps, so the refresh token has to be extracted once from the browser. Username and password are not required.

  1. Open your provider's web player in a Chromium-based browser (e.g. https://www.telenet.tv) and log in.
  2. Press F12 to open Developer Tools → Application (Chrome) / Storage (Firefox) tab.
  3. Expand Local Storage → select the provider's domain.
  4. Find the key containing refresh_token / refreshToken and copy its full value (starts with eyJ, 200+ characters).
  5. Paste it into the account thing's Refresh Token configuration field.

The binding will keep the refresh token current automatically from then on (the backend rotates it on every use), persisting the new value back into the bridge configuration. You should not need to repeat this process unless the token is revoked (e.g. after a very long period offline, or a password change).

# Username/password based authentication (Virgin Media Ireland, Ziggo, UPC Poland)

These providers use your regular account username and password instead. A refresh token is not needed.

# Advanced configuration without provider preset

country, apiUrl and useRefreshToken are advanced parameters. When provider is set, the binding resolves these three from known provider presets and the advanced parameters are ignored. When provider is left empty, the advanced parameters are required and used as-is. This allows supporting a provider that isn't in the providers list yet - a new white-label deployment, or a provider's preprod/test backend.

# box (Thing)

Parameter Required Description
deviceId yes LG Horizon device id (filled in by discovery)
profileId no Household profile to use for favorites/language; defaults to the box default one

# Channels

Channel Type R/W Description
power Switch RW ON wakes the box from standby, OFF puts it into standby
source-type String R linear / replay / nDVR / localDVR / reviewBuffer / VOD / app
channel-name String RW Name of the channel currently playing
channel-number Number RW Logical channel number, send a number to change the channel
favorite-channel-number Number RW Same, limited to favorite channels
program-title String R The primary title for whatever's playing - a show name, movie name, or app name
series-title String R TV series/show name, only set for genuinely episodic content (see table below)
episode-title String R Episode title, falling back to "Episode N" if only a number is known
season Number R Season number, when known
episode Number R Episode number, when known
media-image Image R Poster/background image for the current channel, program, VOD title, or recording
player Player RW PLAY / PAUSE / REWIND / FASTFORWARD
stop Switch W Stop playback and return to live TV
record Switch W Record the current live TV program
channel-up Switch W Move to the next channel
channel-down Switch W Move to the previous channel
arrow-up Switch W Navigate up in the on-screen menu
arrow-down Switch W Navigate down in the on-screen menu
arrow-left Switch W Navigate left in the on-screen menu
arrow-right Switch W Navigate right in the on-screen menu
top-menu Switch W Open the main menu / jump to the top of the screen
info Switch W Show info about the current program
context-menu Switch W Open the context menu (subtitles, favourites, watchlist, ...)
tv Switch W Switch to live TV
enter Switch W Confirm selection / open the channel zap bar
escape Switch W Back / stop and return to live TV
key-code String W Send any remote-control key by its W3C key name (see below)

# Favorite channels

Favorite channels not available with the default profile. You need to create a specific profile and set the box profileId parameter for them to be available.

# Media content

program-title/series-title/episode-title/season/episode/media-image are resolved per source type. program-title is always the primary title for whatever's playing; series-title (and episode-title/season/episode) are only populated when the content is genuinely episodic - a movie or standalone broadcast leaves them undefined, so a rule can check whether series-title is set as a simple "is this a TV series episode" signal.

Source type Title/episode from Image from
live / time shift EPG lookup by event id the channel's own logo/preview image
replay EPG lookup by event id the channel's own logo/preview image
video on demand VOD detail-screen lookup no image
recordings recording detail lookup the channel's own logo/preview image
app (e.g. Netflix) the app's own name (as program-title only) the app's own logo

# key-code values

Any of the values from the box's CPE.KeyEvent protocol, for example: Power, Standby, WakeUp, MediaPlay, MediaPause, MediaPlayPause, MediaStop, MediaRecord, MediaFastForward, MediaRewind, ChannelUp, ChannelDown, Guide, Info, ContextMenu, ArrowUp, ArrowDown, ArrowLeft, ArrowRight, Enter, Escape, Backspace, Red, Green, Yellow, Blue.

# Full Example

# Thing Configuration

Bridge lghorizon:account:home "Telenet Account" [ provider="telenet", refreshToken="eyJ..." ] {
    Thing box livingroom "Living Room Box" [ deviceId="AB12CD34EF56" ]
}

# Item Configuration

Switch  TV_Power           "TV Power"          { channel="lghorizon:box:home:livingroom:power" }
String  TV_Channel         "TV Channel"        { channel="lghorizon:box:home:livingroom:channel-name" }
Number  TV_ChannelNumber   "TV Channel Number" { channel="lghorizon:box:home:livingroom:channel-number" }
String  TV_Program         "TV Program"        { channel="lghorizon:box:home:livingroom:program-title" }
Player  TV_Player          "TV Playback"       { channel="lghorizon:box:home:livingroom:player" }
String  TV_SendKey         "TV Send Key"       { channel="lghorizon:box:home:livingroom:key-code" }

# Advanced Configuration for Unknown Provider

The binding supports manually configuring a provider that is not in the preset list yet.

Bridge lghorizon:account:preprod "Telenet Account (preprod)" [
    country="be",
    apiUrl="https://spark-preprod-be.gnp.cloud.telenet.tv",
    useRefreshToken=true,
    refreshToken="eyJ..."
]

Leaving provider empty is what activates the advanced fields; country and apiUrl are required in that case. The same pattern works for any backend not in the preset list - just supply the three fields that a preset would normally fill in for you.

# Actions

displayMessage(message, duration)

Shows a short text message as an on-screen overlay on the TV. duration is optional and defaults to 10 seconds when omitted or null.

val actions = getActions("lghorizon", "lghorizon:box:home:livingroom")
actions.displayMessage("Doorbell")
actions.displayMessage("Doorbell", 15)

# Console Commands

A number of commands are supported from the console. Accounts and boxes are identified by their own customerId/deviceId, not the openHAB thing UID.

lghorizon accounts
lghorizon profiles [<customerId>]
lghorizon boxes [<customerId>]
lghorizon fingerprint [<customerId>]
lghorizon capture <durationSeconds>

accounts, profiles and boxes take no arguments to list across all accounts, or a customerId to limit the output to one account. boxes lists every device known from the account's REST data, annotated with its box thing's UID for whichever ones actually have one configured ((no thing) otherwise) - useful to find the deviceId for manual thingg configuration.

fingerprint and capture are separate commands with different purposes:

  • fingerprint is a quick, mostly-instant REST-only snapshot (customer/entitlements/channels/service config), plus a passive wait for each box's next (or already-cached) .../status message - not an active request for a fresh one. Useful for a first look at an unsupported provider/device, or to check what the REST side of things looks like.
  • capture <durationSeconds> actively records live traffic for all active accounts over a fixed window: every MQTT status/uiStatus message, every REST call the binding makes. Interact with the box (change channels, rewind, launch an app, start a recording,...) for the duration of the capture to get a meaningful sequence of events. The window duration defaults to, and is limited to, 300s.

Both commands write their output in the user's home lghorizon directory. All personal information is masked.

# Known Limitations / Roadmap

The binding focuses on live status + control. Not implemented:

  • EPG / program guide browsing (apart from emulating the keys on the TV remote)
  • Ad-break detection/skipping
  • localDVR (locally-recorded content) title/image resolution

This binding has been tested with a Telenet IP TV-box. Other set-top boxes and providers may have behaviour not covered by these tests.

# Credits

The Liberty Global Horizon cloud protocol used by this binding (REST auth flow, service discovery, MQTT-over-WebSockets status/control channel) was reverse engineered and documented by:

This binding is an independent Java reimplementation of the protocol those projects document. It shares no code with them. It is not affiliated with or supported by Telenet, Ziggo, Virgin Media, UPC, Sunrise, BASE, or Liberty Global.