NAV Navbar
kotlin java
  • Introduction
  • Overview
  • Security
  • Usage
  • Service discovery
  • API reference
  • Notifications
  • Errors
  • Introduction

    Bbox API STB client is a Kotlin HTTP client library for accessing Bbox Miami API. Those API are available if you have a Bbox Miami or Brooklyn which is a Set-Top-Box (TV box) provided by French Internet Service Provider Bouygues Telecom.

    This library is fully compatible with Kotlin, Java & Android projects.

    Overview

    Bbox Miami Api are composed of

    Box IP/port is broadcasted on local network via MDNS (Multicast DNS) on local network

    architecture

    Security

    These API are secured using :

    For more information check API Security section of this page

    An AppId & AppSecret are necessary to use this library, you can get them by contacting Bouygues Télécom via this contact page

    Usage

    Gradle

    Dependency is available on JCenter or Maven Central with Gradle, from JCenter or MavenCentral :

    repositories {
        jcenter() //or mavenCentral()
    }
    
    dependencies {
        compile 'fr.bmartel:bboxapi-stb:1.1.10' //for JVM
        compile 'fr.bmartel:bboxapi-stb-android:1.1.10' //for Android
    }
    

    Synchronous & Asynchronous

    All methods have an asynchronous & synchronous version. The synchronous version is suffixed with Sync :

    Format

    The format is the same as the one used in Fuel HTTP client library

    The callback function is given in parameter, a Request & Response objects are returned along with a Result object holding the data and a FuelError object in case of failure.

    For instance, for getChannels it will return (Request, Response, Result<List<Channel>, FuelError>)

    In case of Java, an Handler function is used that have 2 callbacks : onSuccess & onError

    A Triple<Request, Response, T> object is returned.

    Service discovery

    Desktop platform

    val bboxapi = BboxApiStb(appId = "YourAppId", appSecret = "YourAppSecret")
    
    bboxapi.startRestDiscovery(findOneAndExit = true, maxDuration = 10000, platform = DesktopPlatform.create()) { eventType, service, error ->
        when (eventType) {
            StbServiceEvent.SERVICE_FOUND -> println("service found : ${service?.ip}:${service?.port}")
            StbServiceEvent.DISCOVERY_STOPPED -> println("end of discovery")
            StbServiceEvent.DISCOVERY_ERROR -> error?.printStackTrace()
        }
    }
    
    BboxApiStb bboxapi = new BboxApiStb("YourAppId", "YourAppSecret");
    
    bboxapi.startRestDiscovery(true, DesktopPlatform.create(), 10000, (stbServiceEvent, stbService, throwable) -> {
        switch (stbServiceEvent) {
            case SERVICE_FOUND:
                System.out.println("service found : " + stbService.getIp() + ":" + stbService.getPort());
                break;
            case DISCOVERY_STOPPED:
                System.out.println("end of discovery");
                break;
            case DISCOVERY_ERROR:
                throwable.printStackTrace();
                break;
        }
        return Unit.INSTANCE;
    });
    

    Android platform

    val bboxapi = BboxApiStb(appId = "YourAppId", appSecret = "YourAppSecret")
    
    bboxapi.startRestDiscovery(findOneAndExit = true, maxDuration = 10000, platform = AndroidPlatform.create()) { eventType, service, error ->
        when (eventType) {
            StbServiceEvent.SERVICE_FOUND -> println("service found : ${service?.ip}:${service?.port}")
            StbServiceEvent.DISCOVERY_STOPPED -> println("end of discovery")
            StbServiceEvent.DISCOVERY_ERROR -> error?.printStackTrace()
        }
    }
    
    BboxApiStb bboxapi = new BboxApiStb("YourAppId", "YourAppSecret");
    
    bboxapi.startRestDiscovery(true, AndroidPlatform.create(), 10000, (stbServiceEvent, stbService, throwable) -> {
        switch (stbServiceEvent) {
            case SERVICE_FOUND:
                System.out.println("service found : " + stbService.getIp() + ":" + stbService.getPort());
                break;
            case DISCOVERY_STOPPED:
                System.out.println("end of discovery");
                break;
            case DISCOVERY_ERROR:
                throwable.printStackTrace();
                break;
        }
        return Unit.INSTANCE;
    });
    

    In order to find Bbox Miami IP adress, use the mDNS broadcast feature exposed by BboxAPI Miami service

    You have to add the following dependency depending on platform :

    Also using startRestDiscovery :

    The last service found is automatically chosen and set in variable bboxapi.restService.

    The list of services found is available in bboxapi.restServiceList. In special environments where there are multiple Bbox Miami or multiple Android TV device with Bbox API STB service installed on the same network, there can be more than one service found if findOneAndExit is set to false.

    API reference

    Display Toast

    Asynchronous

    val toast = ToastRequest(message = "this is a message", pos_y = 500, pos_x = 300, color = "#FF0000")
    
    bboxapi.displayToast(toast) { request, response, result ->
        when (result) {
            is Result.Failure -> {
                result.getException().printStackTrace()
            }
            is Result.Success -> {
                println(response.statusCode)
            }
        }
    }
    
    ToastRequest toastRequest = new ToastRequest("this is a toast", "#FF0000", 500, 300);
    
    bboxapi.displayToast(toastRequest, new Handler<byte[]>() {
        @Override
        public void success(Request request, Response response, byte[] bytes) {
            System.out.println(response.getStatusCode());
        }
    
        @Override
        public void failure(Request request, Response response, FuelError fuelError) {
            fuelError.printStackTrace();
        }
    });
    

    Synchronous

    val toast = ToastRequest(message = "this is a message", pos_y = 500, pos_x = 200, color = "#FF0000")
    
    val (_, res, result) = bboxapi.displayToastSync(toast)
    when (result) {
        is Result.Failure -> {
            result.getException().printStackTrace()
        }
        is Result.Success -> {
            println(res.statusCode)
        }
    }
    
    ToastRequest toastRequest = new ToastRequest("this is a toast", "#FF0000", 500, 300);
    
    Triple<Request, Response, Result<byte[], FuelError>> data = bboxapi.displayToastSync(toastRequest);
    Request request = data.getFirst();
    Response response = data.getSecond();
    Result<byte[], FuelError> obj = data.getThird();
    System.out.println(response.getStatusCode());
    

    Display a Toast message

    Field Type default value Description
    message string "" toast message
    color string null toast text color in hex string format (ex: #FF0000)
    pos_x int null toast X position
    pos_y int null toast Y position

    Get TV channel list

    Asynchronous

    bboxapi.getChannels { _, _, result ->
        when (result) {
            is Result.Failure -> {
                result.getException().printStackTrace()
            }
            is Result.Success -> {
                println(result.get())
            }
        }
    }
    
    bboxapi.getChannels(new Handler<List<Channel>>() {
        @Override
        public void success(Request request, Response response, List<Channel> channels) {
            System.out.println(channels);
        }
    
        @Override
        public void failure(Request request, Response response, FuelError fuelError) {
            fuelError.printStackTrace();
        }
    });
    

    Synchronous

    val (_, _, result) = bboxapi.getChannelsSync()
    when (result) {
        is Result.Failure -> {
            result.getException().printStackTrace()
        }
        is Result.Success -> {
            println(result.get())
        }
    }
    
    Triple<Request, Response, Result<List<Channel>, FuelError>> data = bboxapi.getChannelsSync();
    
    Request request = data.getFirst();
    Response response = data.getSecond();
    Result<List<Channel>, FuelError> obj = data.getThird();
    System.out.println(obj.get());
    

    Get list of TV channel with the following information :

    Field Type Description
    mediaState string state of media play or stop
    mediaTitle string channel name
    positionId int channel position

    Get Application list

    Asynchronous

    bboxapi.getApps { _, _, result ->
        when (result) {
            is Result.Failure -> {
                result.getException().printStackTrace()
            }
            is Result.Success -> {
                println(result.get())
            }
        }
    }
    
    bboxapi.getApps(new Handler<List<Application>>() {
        @Override
        public void success(Request request, Response response, List<Application> channels) {
            System.out.println(channels);
        }
    
        @Override
        public void failure(Request request, Response response, FuelError fuelError) {
            fuelError.printStackTrace();
        }
    });
    

    Synchronous

    val (_, _, result) = bboxapi.getAppsSync()
    when (result) {
        is Result.Failure -> {
            result.getException().printStackTrace()
        }
        is Result.Success -> {
            println(result.get())
        }
    }
    
    Triple<Request, Response, Result<List<Application>, FuelError>> data = bboxapi.getAppsSync();
    
    Request request = data.getFirst();
    Response response = data.getSecond();
    Result<List<Application>, FuelError> obj = data.getThird();
    System.out.println(obj.get());
    

    Get list of Android application installed on Bbox. The information includes the following :

    Field Type Description
    appId string application id
    appName string application name (ex: Youtube)
    packageName string application package name (ex: com.google.youtube)
    component string component intent
    appState string application state (stopped/foreground)
    data string data intent
    leanback boolean if app is an Android TV app
    logoUrl  string path to getAppIcon for this package name

    Get Application info

    Asynchronous

    bboxapi.getAppInfo(packageName = "com.google.android.youtube.tv") { _, _, result ->
        when (result) {
            is Result.Failure -> {
                val ex = result.getException()
                ex.printStackTrace()
            }
            is Result.Success -> {
                val data = result.get()
                println(data)
            }
        }
    }
    
    bboxapi.getAppInfo("com.google.android.youtube.tv", new Handler<List<Application>>() {
        @Override
        public void success(Request request, Response response, List<Application> channels) {
            System.out.println(channels);
        }
    
        @Override
        public void failure(Request request, Response response, FuelError fuelError) {
            fuelError.printStackTrace();
        }
    });
    

    Synchronous

    val (_, _, result) = bboxapi.getAppInfoSync(packageName = "com.google.android.youtube.tv")
    when (result) {
        is Result.Failure -> {
            result.getException().printStackTrace()
        }
        is Result.Success -> {
            println(result.get())
        }
    }
    
    Triple<Request, Response, Result<List<Application>, FuelError>> data = bboxapi.getAppInfoSync("com.google.android.youtube.tv");
    
    Request request = data.getFirst();
    Response response = data.getSecond();
    Result<List<Application>, FuelError> obj = data.getThird();
    System.out.println(obj.get());
    

    Get information about a specific application by package name :

    Field Type Description
    appId string application id
    appName string application name (ex: Youtube)
    packageName string application package name (ex: com.google.youtube)
    component string component intent
    appState string application state (stopped/foreground)
    data string data intent
    leanback boolean if app is an Android TV app
    logoUrl  string path to getAppIcon for this package name

    Get Application icon

    Asynchronous

    bboxapi.getAppIcon(packageName = "com.google.android.youtube.tv") { _, _, result ->
        when (result) {
            is Result.Failure -> {
                result.getException().printStackTrace()
            }
            is Result.Success -> {
                println(result.get().size)
            }
        }
    }
    
    bboxapi.getAppIcon("com.google.android.youtube.tv", new Handler<byte[]>() {
        @Override
        public void success(Request request, Response response, byte[] image) {
            System.out.println(image.length);
        }
    
        @Override
        public void failure(Request request, Response response, FuelError fuelError) {
            fuelError.printStackTrace();
        }
    });
    

    Synchronous

    val (_, _, result) = bboxapi.getAppIconSync(packageName = "com.google.android.youtube.tv")
    when (result) {
        is Result.Failure -> {
            result.getException().printStackTrace()
        }
        is Result.Success -> {
            println(result.get().size)
        }
    }
    
    Triple<Request, Response, Result<byte[], FuelError>> data = bboxapi.getAppIconSync("com.google.android.youtube.tv");
    
    Request request = data.getFirst();
    Response response = data.getSecond();
    Result<byte[], FuelError> obj = data.getThird();
    System.out.println(obj.get().length);
    

    Retrieve Android application icon for a specific package name

    Get current TV channel

    Asynchronous

    bboxapi.getCurrentChannel { _, _, result ->
        when (result) {
            is Result.Failure -> {
                result.getException().printStackTrace()
            }
            is Result.Success -> {
                println(result.get())
            }
        }
    }
    
    bboxapi.getCurrentChannel(new Handler<Media>() {
        @Override
        public void success(Request request, Response response, Media channel) {
            System.out.println(channel);
        }
    
        @Override
        public void failure(Request request, Response response, FuelError fuelError) {
            fuelError.printStackTrace();
        }
    });
    

    Synchronous

    val (_, _, result) = bboxapi.getCurrentChannelSync()
    when (result) {
        is Result.Failure -> {
            result.getException().printStackTrace()
        }
        is Result.Success -> {
            println(result.get())
        }
    }
    
    Triple<Request, Response, Result<Media, FuelError>> data = bboxapi.getCurrentChannelSync();
    
    Request request = data.getFirst();
    Response response = data.getSecond();
    Result<Media, FuelError> obj = data.getThird();
    System.out.println(obj.get());
    

    Get current TV channel with the following information :

    Field Type Description
    mediaService string
    mediaState string media state (stop/play)
    mediaTitle string channel name

    Get volume

    Asynchronous

    bboxapi.getVolume { _, _, result ->
        when (result) {
            is Result.Failure -> {
                result.getException().printStackTrace()
            }
            is Result.Success -> {
                println(result.get())
            }
        }
    }
    
    bboxapi.getVolume(new Handler<Volume>() {
        @Override
        public void success(Request request, Response response, Volume volume) {
            System.out.println(volume);
        }
    
        @Override
        public void failure(Request request, Response response, FuelError fuelError) {
            fuelError.printStackTrace();
        }
    });
    

    Synchronous

    val (_, _, result) = bboxapi.getVolumeSync()
    when (result) {
        is Result.Failure -> {
            result.getException().printStackTrace()
        }
        is Result.Success -> {
            println(result.get())
        }
    }
    
    Triple<Request, Response, Result<Volume, FuelError>> data = bboxapi.getVolumeSync();
    
    Request request = data.getFirst();
    Response response = data.getSecond();
    Result<Volume, FuelError> obj = data.getThird();
    System.out.println(obj.get());
    

    Get volume value

    Get current TV channel with the following information :

    Field Type Description
    volume string volume value (yes this is a string !?)

    Set volume

    Asynchronous

    bboxapi.setVolume(volume = 10) { _, response, result ->
        when (result) {
            is Result.Failure -> {
                result.getException().printStackTrace()
            }
            is Result.Success -> {
                println(response.statusCode)
            }
        }
    }
    
    bboxapi.setVolume(10, new Handler<byte[]>() {
        @Override
        public void success(Request request, Response response, byte[] body) {
            System.out.println(response.getStatusCode());
        }
    
        @Override
        public void failure(Request request, Response response, FuelError fuelError) {
            fuelError.printStackTrace();
        }
    });
    

    Synchronous

    val (_, response, result) = bboxapi.setVolumeSync(volume = 100)
    when (result) {
        is Result.Failure -> {
            result.getException().printStackTrace()
        }
        is Result.Success -> {
            println(response.statusCode)
        }
    }
    
    Triple<Request, Response, Result<byte[], FuelError>> data = bboxapi.setVolumeSync(100);
    
    Request request = data.getFirst();
    Response response = data.getSecond();
    Result<byte[], FuelError> obj = data.getThird();
    System.out.println(response.getStatusCode());
    

    Set volume

    Field Type Description
    volume int volume value

    Start application

    Asynchronous

    bboxapi.startApp(packageName = "com.google.android.youtube.tv") { _, response, result ->
        when (result) {
            is Result.Failure -> {
                result.getException().printStackTrace()
            }
            is Result.Success -> {
                println(response.statusCode)
            }
        }
    }
    
    bboxapi.startApp("com.google.android.youtube.tv", new Handler<byte[]>() {
        @Override
        public void success(Request request, Response response, byte[] body) {
            System.out.println(response.getStatusCode());
        }
    
        @Override
        public void failure(Request request, Response response, FuelError fuelError) {
            fuelError.printStackTrace();
        }
    });
    

    Synchronous

    val (_, response, result) = bboxapi.startAppSync(packageName = "com.google.android.youtube.tv")
    
    when (result) {
        is Result.Failure -> {
            result.getException().printStackTrace()
        }
        is Result.Success -> {
            println(response.statusCode)
        }
    }
    
    Triple<Request, Response, Result<byte[], FuelError>> data = bboxapi.startAppSync("com.google.android.youtube.tv");
    
    Request request = data.getFirst();
    Response response = data.getSecond();
    Result<byte[], FuelError> obj = data.getThird();
    System.out.println(response.getStatusCode());
    

    Start Android application by package name

    Field Type Description
    packageName string application package name (ex: com.google.youtube)

    Custom HTTP request

    Asynchronous

    bboxapi.createCustomRequest(bboxapi.manager.request(method = Method.GET, path = "/applications")) { _, _, result ->
        when (result) {
            is Result.Failure -> {
                result.getException().printStackTrace()
            }
            is Result.Success -> {
                println(String(result.get()))
            }
        }
    }
    
    bboxapi.createCustomRequest(bboxapi.getManager().request(Method.GET, "/applications", null), new Handler<byte[]>() {
        @Override
        public void success(Request request, Response response, byte[] data) {
            System.out.println(new String(data));
        }
    
        @Override
        public void failure(Request request, Response response, FuelError fuelError) {
            fuelError.printStackTrace();
        }
    });
    

    Synchronous

    val (_, _, result) = bboxapi.createCustomRequestSync(bboxapi.manager.request(method = Method.GET, path = "/applications"))
    when (result) {
        is Result.Failure -> {
            result.getException().printStackTrace()
        }
        is Result.Success -> {
            println(String(result.get()))
        }
    }
    
    Triple<Request, Response, Result<byte[], FuelError>> data = bboxapi.createCustomRequestSync(bboxapi.getManager().request(Method.GET, "/applications", null));
    
    Request request = data.getFirst();
    Response response = data.getSecond();
    Result<byte[], FuelError> obj = data.getThird();
    System.out.println(new String(obj.get()));
    

    Create your own HTTP request, this can be useful for not relying on the library implementation

    All request construction are prefixed with http://$boxIp:$boxRestPort/api.bbox.lan/v0 if host is not specified

    Notifications

    BboxAPI Miami service dispatch notifications via WebSocket on port 9090. This library abstracts all the flow involving app registering, subscribing to events & opening websocket. This flow is described here

    Subscribe notifications

    val notificationChannel = bboxapi.subscribeNotification(
            appName = "myApplication",
            resourceList = listOf(Resource.Application, Resource.Media, Resource.Message),
            listener = object : BboxApiStb.WebSocketListener {
    
                override fun onOpen() {
                    println("websocket opened")
                }
    
                override fun onClose() {
                    println("websocket closed")
                }
    
                override fun onApp(app: AppEvent) {
                    println("application event : $app")
                }
    
                override fun onMedia(media: MediaEvent) {
                    println("channel change event : $media")
                }
    
                override fun onMessage(message: MessageEvent) {
                    println("message event : $message")
                }
    
                override fun onError(error: BboxApiError) {
                    println("error : $error")
                }
    
                override fun onFailure(throwable: Throwable?) {
                    throwable?.printStackTrace()
                }
            })
    val (_, _, result) = notificationChannel.subscribeResult
    if (result is Result.Failure) {
        result.error.printStackTrace()
    } else {
        println("subscribed on channelId ${notificationChannel.channelId} & appId ${notificationChannel.appId}")
    }
    
    List<Resource> resourceList = new ArrayList<>();
    resourceList.add(Resource.Application);
    resourceList.add(Resource.Media);
    resourceList.add(Resource.Message);
    
    NotificationChannel notificationChannel = bboxapi.subscribeNotification(
            "myApplication",
            resourceList,
            new BboxApiStb.WebSocketListener() {
                @Override
                public void onOpen() {
                    System.out.println("websocket opened");
                }
    
                @Override
                public void onClose() {
                    System.out.println("websocket closed");
                }
    
                @Override
                public void onError(@NotNull BboxApiError error) {
                    System.out.println("error : " + error);
                }
    
                @Override
                public void onMedia(@NotNull MediaEvent media) {
                    System.out.println("channel change event : " + media);
                }
    
                @Override
                public void onApp(@NotNull AppEvent app) {
                    System.out.println("application event : " + app);
                }
    
                @Override
                public void onMessage(@NotNull MessageEvent message) {
                    System.out.println("message event : " + message);
                }
    
                @Override
                public void onFailure(@Nullable Throwable throwable) {
                    throwable.printStackTrace();
                }
            });
    
    Triple<Request, Response, Result<byte[], FuelError>> result = notificationChannel.getSubscribeResult();
    
    if (result.component3().component2() != null) {
        result.component3().component2().printStackTrace();
    } else {
        System.out.println("subscribed with resource on channelId " +
                notificationChannel.getChannelId() +
                " & appId " + notificationChannel.getAppId());
    }
    

    To listen for notifications use subscribeNotification with a list of Resource including the following :

    Field Type Description
    appName string a name for your application
    resourceList List list of resources to subscribe
    listener BboxApiStb.WebSocketListener websocket event listener

    The underlying flow registers an "application" & subscribes notifications, an appID/channelID is returned from :

    These appID/channelID can be used to send notification, check "Send notification" section

    When you are done listening to notification call closeWebsocket to close the websocket.

    Unsubscribe all channels

    To unsubscribe all channels ID call unsubscribeAllSync. This will get all distinct opened channels & unsubscribe each of them

    bboxapi.unsubscribeAllSync()
    
    bboxapi.unsubscribeAllSync();
    

    Send notification

    Asynchronous

    bboxapi.sendNotification(
            channelId = "15233003603520.7238189308864336-0.0718767910445014",
            appId = "15233003603520.7238189308864336",
            message = "some message") { _, response, result ->
        when (result) {
            is Result.Failure -> {
                result.getException().printStackTrace()
            }
            is Result.Success -> {
                println("message sent")
            }
        }
    }
    
    bboxapi.sendNotification(
            "15233003603520.7238189308864336-0.0718767910445014",
            "15233003603520.7238189308864336",
            "some message", new Handler<byte[]>() {
                @Override
                public void success(Request request, Response response, byte[] bytes) {
                    System.out.println("message sent");
                }
    
                @Override
                public void failure(Request request, Response response, FuelError fuelError) {
                    fuelError.printStackTrace();
                }
            }
    );
    

    Synchronous

    val (_, _, result) = bboxapi.sendNotificationSync(
            channelId = "15233003603520.7238189308864336-0.0718767910445014",
            appId = "15233003603520.7238189308864336",
            message = "some message")
    when (result) {
        is Result.Failure -> {
            result.getException().printStackTrace()
        }
        is Result.Success -> {
            println("message sent")
        }
    }
    
    Triple<Request, Response, Result<byte[], FuelError>> result = bboxapi.sendNotificationSync(
            "15233003603520.7238189308864336-0.0718767910445014",
            "15233003603520.7238189308864336",
            "some message"
    );
    if (result.component3().component2() != null) {
        result.component3().component2().printStackTrace();
    } else {
        System.out.println("message sent");
    }
    

    You can send a notification to a pair channelId / appId.

    Field Type Description
    channelId string ID retuned by subscribing the previous "application" for specific resources
    appId string ID returned by registering an "application" for notifications
    message string message to send

    Errors

    check exception type & response status code on failure :

    bboxapi.getApps { _, response, result ->
        when (result) {
            is Result.Failure -> {
                val ex = result.getException()
                when {
                    ex.exception is HttpException -> println("http error : ${response.statusCode}")
                    else -> ex.printStackTrace()
                }
            }
            is Result.Success -> {
                val data = result.get()
                println(data)
            }
        }
    }
    
    bboxapi.getApps(new Handler<List<Application>>() {
        @Override
        public void failure(Request request, Response response, FuelError error) {
            if (error.getException() instanceof HttpException) {
                System.out.println("http error : " + response.getStatusCode());
            } else {
                error.printStackTrace();
            }
        }
    
        @Override
        public void success(Request request, Response response, List<Application> data) {
            System.out.println(data);
        }
    });
    

    FuelError can be checked for exceptions, for instance :

    Exception description
    HttpException a non 2XX HTTP response was received, check the status code from the response

    Among HttpException, you can find the following :

    Error Code Meaning
    400 Bad Request -- request format is invalid
    404 Not Found -- endpoint doesn't exist (check it starts with http://$IP:$PORT/api.bbox.lan/v0)