Last Updated: 22 November 2021

Nuget Nuget
Explore code with Fuget
Install-Package ParkSquare.SkyTv

SkyQ TV Box C# Remote Control & Record

Build Status

Background

Control SkyQ set-top box over the network. Supports all remote control commands including power on/off, change channel, play/pause/stop/record, show program guide, box office and UI navigation. Retrieve box information such as device manufacturer, model and software version. View all Sky electronic programme guide (EPG) channel information and programme guide data, search for what's on now/next. Schedule recordings, list and delete recordings.

Getting Started

There are essentially three components: DeviceDiscovery, ApiClient, RemoteControl. Note that this package only works with SkyQ boxes, support for the old SkyHD and Sky+ boxes was removed from v4 onwards.

Device Discovery (UPnP)

This uses UPnP/SSDP to find devices on your network. The IP addresses of any that are identified as Sky boxes are returned. Devices will periodically advertise their existence, so you need to choose a timeframe long enough to receive them - usually a few seconds is enough.

Get Client IP Address

First, get all IP addresses for the current device (e.g. the dev machine you're running on). You can skip this section and hardcode an IP address if preferred.

var clientIpAddresses = new List<IPAddress>();

foreach (var netInterface in NetworkInterface.GetAllNetworkInterfaces())
{
    if (netInterface.OperationalStatus != OperationalStatus.Up) continue;

    var ipProps = netInterface.GetIPProperties();

    foreach (var addr in ipProps.UnicastAddresses)
    {
        if (addr.Address.AddressFamily == AddressFamily.InterNetwork)
        {
            clientIpAddresses.Add(addr.Address);
        }
    }
}

Find SkyQ Boxes

Now find the SkyQ boxes on the network.

IReadOnlyCollection<IPAddress> boxIpAddresses;

// Find SkyQ boxes on the network

using (var deviceDiscovery = new DeviceDiscovery())
{
    Console.WriteLine("Looking for SkyQ boxes on network...");

    boxIpAddresses = await deviceDiscovery.FindSkyBoxesAsync(clientIpAddresses, TimeSpan.FromSeconds(3));
}

if (!boxIpAddresses.Any())
{
    Console.WriteLine("No SkyQ boxes found!");
}

Set Top Box Control

An API running on each SkyQ box exposes certain information and functions. An ApiClient instance can be created directly, or if using Dependency Injection, using the ApiClientFactory. The recommended method is to automatically discover the device IP address using the method above, but this is not mandatory - it can also be created using a standard HttpClient and an IPAddress you construct yourself:

var apiClient = new ApiClient(_httpClient, ipAddress);

Once you have an ApiClient, you can query details about the box, get, schedule and remove PVR recordings.

GetBoxDetailsAsync()

This call returns various bits of information about your SkyQ box, such as MAC address, software versions, make and model, and whether it is currently in standby mode or not.

var restClient = new RestClient(new HttpClient());
var apiClientFactory = new ApiClientFactory(restClient);

foreach (var boxIpAddress in boxIpAddresses)
{
    Console.WriteLine($"{boxIpAddress}");

    // Get box info

    var apiClient = apiClientFactory.CreateClient(boxIpAddress);
    var box = await apiClient.GetBoxDetailsAsync();

    Console.WriteLine($"{box.IpAddress} {box.MacAddress} {box.Manufacturer} {box.ModelNumber} " +
    $"Up time: {box.SystemUptime} 4K Enabled: {box.UhdCapable} {box.PowerStatus}");
}

Sample output:

10.10.0.104 78:3E:53:XX:XX:XX Sky Q170.000.23.00L (5dhxxxx) Up time: 11:04:15 4K Enabled: True Standby

GetRecordingsAsync()

This call gets a list of all PVR recordings on the box, which includes ones that have been deleted but not yet erased. If the item has been deleted, the IsDeleted flag will be set, the DeletedOn property will be present, and the date/time at which the item will be permanently removed is in the ExpiresOn property. The `PvrId is a unique identifier for that particular recording on your box, and can be used by other calls.

var restClient = new RestClient(new HttpClient());
var apiClientFactory = new ApiClientFactory(restClient);

foreach (var boxIpAddress in boxIpAddresses)
{
    Console.WriteLine($"{boxIpAddress}");

    var recordings = await apiClient.GetRecordingsAsync();

    foreach (var recording in recordings)
    {
        Console.WriteLine($"{recording.Title} Season {recording.SeasonNumber} Episode {recording.EpisodeNumber}");
    }
}

Sample output:

Brassic Season 1 Episode 1
Brassic Season 1 Episode 2
Brassic Season 1 Episode 3
The Fresh Prince Of Bel-Air Season 4 Episode 7
The Fresh Prince of Bel-Air Season 4 Episode 7
The Fresh Prince Of Bel-Air Season 4 Episode 8

RecordProgrammeAsync()

This method tells the PVR to record a programme, or 'events' as they are known in Sky parlance. The EventId passed in to identify the programme can be found in the EPG listings search discussed later. An optional flag called SeriesLink can be specified to tell the box to link all future episodes and record them automatically. This option is false by default, so only the individual programme will be recorded.

var success = await apiClient.RecordProgrammeAsync("E708-7a0", true);

CancelRecordingAsync()

This method stops an in-progress recording, but does will not delete the partially recorded programme, instead leaving it on the box to watch.

var success = await apiClient.CancelRecordingAsync("pvr-1234");

DeleteRecordingAsync()

This will delete the recording. By default, it will only soft delete the recording. There is an optional PermanentlyDelete parameter that will force the item to be erased from the disk immediately.

var success = await apiClient.DeleteRecordingAsync("pvrid1234", true);

GetStorageStatusAsync()

This call returns the amount of disk space available and used.

var storageStatus = await apiClient.GetStorageStatusAsync();
Console.WriteLine($"{storageStatus.UsedMb} Mb used of {storageStatus.QuotaMb} Mb ({storageStatus.PercentageUsed} %)");

Sample output:

20262 Mb used of 675048 Mb (3.00 %)

GetTimeAsync()

This call returns the current time according to the set-top box, in both UTC and local timezones.

var time = await apiClient.GetTimeAsync();
Console.WriteLine($"{time.Utc} {time.Local}");

GetServicesAsync()

This call returns a list of all channels on the box, including audio only (radio) services.

var services = await apiClient.GetServicesAsync();

foreach (var service in services)
{
    Console.WriteLine($"{service.ChannelId} " +
                      $"{service.ChannelNumber} " +
                      $"{service.Description} " +
                      $"{service.PictureQuality} " +
                      $"{service.ServiceType}");
}

Sample output:

Box is in standby, switching on....
2104 101 BBC One Yorks StandardDefinition Video
2075 102 BBC Two HD HighDefinition Video
1044 103 ITV HD HighDefinition Video
1624 104 Channel 4 StandardDefinition Video
4058 105 Channel 5 HD HighDefinition Video

Remote Control

The RemoteControl object allows remote control of the SkyQ box over your network by IP address. The available commands more or less mimic those found on the physical remote control, a list of which can be found in the RemoteCommand enum. Note that remote commands are ignored if the box is in standby. You can use the PowerStatus property from GetBoxDetailsAsync() above to determine if the box needs powering on before sending commands. The example below switches the box to Channel 409 (Sky Sports News), and brings the box out of standby if necessary using the result of GetBoxDetailsAsync() above.

var remoteControl = new RemoteControl(boxIpAddress);

if (box.PowerStatus == PowerStatus.Standby)
{
	Console.WriteLine("Box is in standby, switching on....");
	await remoteControl.SendCommandAsync(RemoteCommand.Power, RemoteCommand.Backup);
}

await remoteControl.SendCommandAsync(
	RemoteCommand.Number4, 
	RemoteCommand.Number0,
	RemoteCommand.Number9);