Skip to content

TVS IFrame API

Starting with Television Simulator 5.6.0, developers can access options from the TVS config file to use in their web applications. This allows TVS users to have an easy way to pass JSON data from TVS to your web application.

We use Window.postMessage() and IFrames to enable two-way communication between TVS and your application.

Since: TVS 5.6.0

Your application is in control of when to start the handshake process with TVS; this is so you can ensure your app is fully loaded and ready before attempting to communicate with TVS.

To perform the handshake, your application should send a TVS.Ready message to the parent window (TVS) using the following code:

window.parent.postMessage({'type': 'TVS.Ready'}, '*');

Once TVS receives this message, it responds with TVS.Options containing the application configuration and the enabled deep-integration capabilities.

window.addEventListener('message', (event) => {
if (event.data.type === 'TVS.Options') {
const options = JSON.parse(event.data.options);
const capabilities = event.data.capabilities;
// Use the options and enable APIs based on the granted capabilities.
}
});

event.data.options will contain a JSON string of the IFrame options defined in the TVS config file. event.data.capabilities will contain an object describing the deep-integration APIs enabled for the IFrame.

Since: TVS 6.2.0

The capabilities property on TVS.Options tells the IFrame which APIs it can access based on the deepIntegration settings in the TVS config file.

{
type: 'TVS.Options',
options: '{}',
capabilities: {
channelInfo: 'current-channel',
canRequestTermination: true
}
}

TVS always includes this property in TVS.Options. When no deep-integration permissions are configured, capabilities is an empty object. Omitted properties should be treated as disabled.

The message reports configured permissions. Contextual requirements still apply; for example, canRequestTermination: true only has an effect when the IFrame is running as scheduled content with a completion callback.

Since: TVS 5.6.0

The options JSON string should resolve to a dictionary of key-value pairs. The keys will be strings.

The values can be of various types, including:

  • Strings
  • Numbers
  • Booleans
  • Arrays
  • Nested objects

Imagine you’re building a weather application that can display information for different cities and you’d like to allow users to set their preferred locations via TVS. You might decide to accept a list of city names as an IFrame option.

Suppose you wanted to accept a list of cities like this:

{
"cities": ["New York", "Los Angeles", "Chicago"]
}

You’ll instruct your users to add this YAML to their TVS config file:

channels:
- number: 1
name: "My Cool Weather App"
iframe:
src: "https://mycoolweatherapp.example.com"
options:
cities:
- "New York"
- "Los Angeles"
- "Chicago"

Upon handshaking, your application will receive an event like this:

{
type: 'TVS.Options',
options: '{"cities":["New York","Los Angeles","Chicago"]}',
capabilities: {}
}

Since: TVS 5.7.0

In addition to TVS.Options, your application can also listen for other status messages from TVS:

  • TVS.Volume: Reports the current volume level (from 0 to 100).
  • TVS.Muting: Indicates whether the TV is muted.
  • TVS.Power: Indicates whether the TV is on or off.

These messages will be sent automatically during the initial handshake and whenever the state changes in TVS.

if (window.parent && window.parent !== window) {
// Only attempt to handshake with TVS if we're in an IFrame.
// Set up an event listener before attempting a handshake so that you're ready to receive the reply.
addEventListener('message', function(event) {
// These are the expected events from TVS when sending `TVS.Ready`
switch(event?.data?.type) {
case 'TVS.Options':
console.log('Received options string:', event.data.options);
const parsedOptions = JSON.parse(event.data.options);
const capabilities = event.data.capabilities;
console.log('Parsed options object:', parsedOptions);
// Use the options and enable APIs based on the granted capabilities.
break;
case 'TVS.Volume': // Sent when volume changes and during handshake
const volume = parseFloat(event.data.volume);
// Update your app's volume accordingly; event.data.volume is between 0 and 100
break;
case 'TVS.Muting': // Sent when mute state changes and during handshake
if (typeof event.data.isMuting === 'boolean') {
const isMuting = event.data.isMuting;
// Update your app's mute state accordingly
}
break;
case 'TVS.Power': // Sent when power state changes and during handshake
if (typeof event.data.isOn === 'boolean') {
const isOn = event.data.isOn;
// Update your app's power state accordingly
}
break;
// As the API evolves, more message types may be added here.
default:
// Ignore other messages
break;
}
});
// To attempt the handshake, post `TVS.Ready` event:
window.parent.postMessage({'type': 'TVS.Ready'}, '*');
}

Since: TVS 6.2.0

TVS can share current-channel information with an embedded application and, when explicitly allowed, answer guide listing queries. These APIs require the IFrame engine’s deepIntegration.channelInfo option.

iframe:
src: "https://guide.example.com"
deepIntegration:
channelInfo: "current-channel"

Set channelInfo to:

  • "current-channel" to receive information and request listings for the channel containing the IFrame.
  • "all-channels" to receive current-channel information and request listings for a range of channels.
  • false, or omit the option, to disable both APIs.

The application must complete the normal TVS.Ready handshake before TVS sends this information.

When channelInfo is "current-channel" or "all-channels", TVS sends TVS.CurrentChannel during the initial handshake. TVS checks the current guide data every five seconds and sends another message whenever it changes.

{
type: 'TVS.CurrentChannel',
channel: {
number: '12.1',
name: 'Example Channel',
abbr: 'EXAMPLE',
icon: '/icons/example.png',
info: {
title: 'Example Series',
subtitle: 'Example Program',
summary: 'An example program description.',
startTime: new Date('2026-07-23T20:00:00Z'),
endTime: new Date('2026-07-23T20:30:00Z'),
isStereo: true
}
}
}

channel.info is a GuideData object describing what is currently on. Optional channel metadata is omitted when it isn’t set. startTime and endTime are Date objects when set.

An application with channelInfo: "current-channel" or "all-channels" can post TVS.GetListings with a time window:

window.parent.postMessage({
type: 'TVS.GetListings',
channelRange: {
min: 2,
max: '12.1'
},
startTime: new Date(),
endTime: new Date(Date.now() + 90 * 60 * 1000)
}, '*');

startTime and endTime may be Date objects, timestamps, or date strings. endTime must be later than startTime.

When only given access to the current channel (channelInfo: "current-channel"), TVS ignores channelRange and returns only the channel containing the IFrame. This can be used to retrieve upcoming programs without granting access to the rest of the channel lineup.

When allowed access to all channels (channelInfo: "all-channels"), the channel range is optional. Its min and max values are inclusive and support both whole channels and dotted subchannels. When min is omitted, TVS starts at channel 0, matching the built-in guide’s default behavior. Hidden channels are not returned.

TVS replies with TVS.Listings. The listings property contains one entry per channel, sorted by channel number:

{
type: 'TVS.Listings',
listings: [
{
number: '2',
abbr: 'NEWS',
listings: [
{
title: 'Example Series',
subtitle: 'Evening News',
startTime: new Date('2026-07-23T20:00:00Z'),
endTime: new Date('2026-07-23T20:30:00Z')
}
]
}
]
}

TVS silently ignores TVS.GetListings when channel information access is disabled, the IFrame has not completed its handshake, or the applicable request arguments are invalid. A malformed channelRange is still ignored when using "current-channel" because the argument is not used in that mode.

Use TVS.GetUpNext when an application needs a small number of sequential programs rather than all listings in a time window:

window.parent.postMessage({
type: 'TVS.GetUpNext',
numberOfPrograms: 3,
includeCurrentProgram: true
}, '*');

The request accepts:

  • numberOfPrograms: A required positive integer. This is the maximum number of programs returned, including the current program when includeCurrentProgram is true.
  • includeCurrentProgram: An optional boolean that defaults to false. When true, the resolved current program is the first item.
  • channelNumber: An optional whole channel or dotted subchannel number. This is only allowed with channelInfo: "all-channels". When omitted, TVS uses the channel containing the IFrame.

If channelNumber is included with "current-channel" access, TVS silently ignores the entire request rather than exposing information about another channel. An invalid or unknown channel number is also ignored.

TVS replies with TVS.UpNext. programs contains the available programs in chronological order and may contain fewer items than requested when the guide database does not have enough upcoming entries:

{
type: 'TVS.UpNext',
channelNumber: '12.1',
programs: [
{
title: 'Example Series',
subtitle: 'Example Program',
startTime: new Date('2026-07-23T20:00:00Z'),
endTime: new Date('2026-07-23T20:30:00Z')
},
{
title: 'Example Series',
subtitle: 'The Next Example Program',
startTime: new Date('2026-07-23T20:30:00Z'),
endTime: new Date('2026-07-23T21:00:00Z')
}
]
}

Upcoming entries come from TVS’s synchronized guide database. Channels configured only with inline program information or a current-program URL do not have future entries; they return only the current program when it is requested.

Since: TVS 6.0.0

TVS will send a TVS.Terminate message to your application when the user exits the channel. This allows your application to perform any necessary cleanup, such as stopping media playback or saving state. There is a 500ms grace period after receiving this message before TVS will forcefully close the IFrame, so make sure to complete any cleanup tasks within that time frame.

window.addEventListener('message', (event) => {
if (event.data.type === 'TVS.Terminate') {
// Perform cleanup tasks here
window.parent.postMessage({type: 'TVS.Terminated'}, '*');
}
});

Send TVS.Terminated after cleanup so TVS can finish immediately. If the application does not acknowledge the message, TVS continues after the 500ms grace period.

Since: TVS 6.2.0

An embedded application can request the end of its own scheduled content when deepIntegration.canRequestTermination is enabled:

iframe:
src: "https://example.com/scheduled-content"
deepIntegration:
canRequestTermination: true

When the application is ready to finish, post TVS.RequestTermination:

window.parent.postMessage({type: 'TVS.RequestTermination'}, '*');

TVS responds with the normal TVS.Terminate message and waits for TVS.Terminated or the 500ms grace period. It then advances or dismisses the scheduled content. Each IFrame instance can complete this sequence only once.

The request is silently ignored when:

  • canRequestTermination is not true.
  • The application has not completed the TVS.Ready handshake.
  • The IFrame is not running in a context with a completion callback. For example, a top-level channel IFrame has nowhere to advance, while an IFrame inside a loop can advance to the next item.

When implementing the IFrame API, it’s crucial to consider security implications:

  • Always validate and sanitize the data received from TVS to prevent potential security vulnerabilities.
  • Be cautious when using the wildcard '*' in the postMessage method; consider specifying the exact origin of TVS if known. (If you are hosting TVS yourself and know where it will be accessed from, you can specify that URL instead of '*'. If you’d like anyone to be able to use your app with any TVS instance, you can leave it as '*'.)
  • Implement proper error handling to manage unexpected messages or data formats.

To help you get started with the TVS IFrame API, we’ve created a simple test application that demonstrates how to use the API to receive options from TVS.

It’s a single-file HTML file to make it easy to read. View the source code to see how it works and use it as a reference for building your own applications.