Creating Custom Roku Events and Commands

When you create a Cloud app, some events and commands are sent automatically. We call these standard events and commands.

You can also create your own custom events and commands. For example, you may want to listen for specific Key events that aren’t supported by default.

To add custom events and commands, you must modify the client (BrightScript). To modify a specific BrightScript file, you must include it in the <project>/client folder.

The original BrightScript files for the client are part of You.i Platform. You can find these files in the ~/youiengine/<version>/src/cloud folder.

Custom events

For custom events we leverage the existing communication path and send our own custom JSON from the client using the sendObjectAsJSON method. Then the server has to listen for that custom event and respond appropriately.

Follow these steps to add custom events:

Copy the BrightScript files

Copy the BrightScript files where you want to send the custom events inside the <project>/client folder.

Copying a file from the You.i Platform source folder

This example shows how to copy the main_implementation.brs file.

Open a terminal window and run the following commands inside your <project>/client folder:

mkdir source
cp ~/youiengine/<version>/src/cloud/source/main_implementation.brs ./source

Edit the <project>/client/source/main_implementation.brs file to invoke the sendObjectAsJSON method, passing an object with the custom event name and any additional parameters if needed.

Here’s how to send a custom event from a file inside the client/source folder:

sendObjectAsJSON({ event: "CUSTOM_EVENT", payload: "PAYLOAD" })

Copying a file from the You.i Platform components folder

This example shows how to copy the VideoPlayer.brs file.

Copy the BrightScript file. Open a terminal window and run the following command from your <project>/client folder:

cp ~/youiengine/<version>/src/cloud/components/VideoPlayer.brs ./components

Add an XML interface field by modifying the main_implementation.brs file.

  1. Copy the main_implementation.brs from the source folder to the <project>/client folder:

    cd client
    mkdir source
    cp ~/youiengine/<version>/src/cloud/source/main_implementation.brs ./source
    
  2. Edit the showSceneGraphScreen method from the <project>/client/source/main_implementation.brs file to add the following lines:

    sub showSceneGraphScreen()
        ' ...
        m.global.addField("customEvent", "assocarray", true)
        m.global.ObserveField("customEvent", m.port)
        '...
    end sub
    
  3. Observe the given field with a callback method.

    Edit the processNodeEvent method from the <project>/client/source/main_implementation.brs file to add a new field handler callback:

    function processNodeEvent(event as object) as boolean
        fieldHandlers = {
        ' ...
        "customEvent": onCustomEvent
        }
        ' ...
    end function
    
  4. Write the callback method to handle the event.

    Copy the customCommands.brs file from the source folder to the <project>/client/source folder:

    cd client
    cp ~/youiengine/<version>/src/cloud/source/customCommands.brs ./source
    

    Add the field handler callback method inside the <project>/client/source/customCommands.brs file:

    REM Functions to run custom commands
    sub initCustomCommands()
    end sub
    function onCustomEvent(event as object) as boolean
        sendObjectAsJSON(event.GetData())
        return true
    end function
    
  5. Mutate the global XML interface field to dispatch a message.

    Edit the <project>/client/components/VideoPlayer.brs file to dispatch the custom event. Instead of using the sendObjectAsJSON function, you should mutate the global customEvent field, that will trigger the custom event callback.

    m.global.customEvent = { event: "CUSTOM_EVENT", payload: "PAYLOAD" }
    

Side-load the client to your Roku device

To side-load the client use the following command:

youi-tv roku-client -l -s <Roku device IP address> -u rokudev:<password>

Create a native module to listen for the custom event

Add two new files inside the <project>/youi/src folder: CustomEvent.cpp and CustomEvent.h.

Example: Native module source file

This example shows how to create CustomEvent.cpp.

#include "CustomEvent.h"
#include <youireact/NativeModuleRegistry.h>
#ifdef YI_CLOUD_SERVER
#include <cloud/YiCloud.h>
#endif
static const std::string YI_EVENT_NAME = "CUSTOM_EVENT";
const CYIString ROKU_EVENT_NAME = "CUSTOM_EVENT";
YI_RN_INSTANTIATE_MODULE(CustomEvent, yi::react::EventEmitterModule);
YI_RN_REGISTER_MODULE(CustomEvent);
CustomEvent::CustomEvent()
{
SetSupportedEvents
({
    YI_EVENT_NAME
});
#ifdef YI_CLOUD_SERVER
m_EventHandlerId = CYICloud::GetInterface().RegisterDataListener(
    ROKU_EVENT_NAME,
    [this](CYIString eventId, const yi::rapidjson::Document *pEvent) {
        static_cast<void>(eventId);
        CYIParsingError jsonError;
        CYIString payload;
        CYIRapidJSONUtility::GetStringField(pEvent, "payload", payload, jsonError);
        EmitEvent(YI_EVENT_NAME, folly::dynamic::object("payload", ToDynamic(payload)));
        return true;
    });
#endif
}
CustomEvent::~CustomEvent() {
#ifdef YI_CLOUD_SERVER
    CYICloud::GetInterface().UnregisterDataListener(ROKU_EVENT_NAME, m_EventHandlerId);
#endif
}

Example: Native module header file

This example shows how to add CustomEvent.h.

#include <youireact/modules/EventEmitter.h>
class YI_RN_MODULE(CustomEvent, yi::react::EventEmitterModule)
{
public:
    CustomEvent();
    ~CustomEvent();
    YI_RN_EXPORT_NAME(CustomEvent)
private:
    uint64_t m_EventHandlerId;
};

Edit the CMake file

Edit the <project>/youi/SourceList.cmake file to add the CustomEvent.cpp and CustomEvent.h files.

Here’s an example SourceList.cmake file:

# =============================================================================
# © You i Labs Inc. All rights reserved.
set (YI_PROJECT_SOURCE
    src/App.cpp
    src/AppFactory.cpp
    src/CustomEvent.cpp
)

set (YI_PROJECT_HEADERS
    src/App.h
    src/CustomEvent.h
)

Handle the custom event on the server side

The following code sample shows how to handle a custom event:

import { NativeEventEmitter, NativeModules } from 'react-native';
const eventEmitter = new NativeEventEmitter(NativeModules.CustomEvent);
// ...
componentDidMount() {
   this.eventSubscription = eventEmitter.addListener("CUSTOM_EVENT", (data) => {
        console.log(`event: CUSTOM_EVENT, payload: ${data.payload}`);
    });
}
componentWillUnmount() {
    this.eventSubscription.remove();
}

Run the server

Run the server. See Building and Running a Roku App.

Custom event logs

You should see the following logs each time you send the custom event.

On the BrightScript Console:

[2021-12-02 04:50:38.201Z] Sending {"event":"CUSTOM_EVENT","payload":"PAYLOAD"} over TCP connection

On the server:

2021-12-01 20:50:23.226 rokuApp[39864:3091753] I/JavaScript:   1: event: CUSTOM_EVENT, payload: PAYLOAD

Custom commands

To add custom commands, you must modify the client and inject commands that can be invoked using the invokeCommand method.

Enable the Cloud Solution

Enable the Cloud Solution through the Cloud Module.

Copy the customCommands file

Copy the customCommands.brs file from the components folder to the <project/client> folder.

cd client
mkdir source
cp ~/youiengine/<version>/src/cloud/source/customCommands.brs ./source

Create a new method

Create a new method inside the <project>/client/source/customCommands.brs file.

Register the new command

Register the new command calling the addCustomCommand method inside the initCustomCommands method.

The following code sample shows how to handle a custom command:

REM Functions to run custom commands
sub initCustomCommands()
addCustomCommand("CUSTOM_COMMAND", handleCustomCommand)
end sub
sub handleCustomCommand(params as object)
logMessage("command: CUSTOM_COMMAND, payload: " + params.payload)
end sub

Side-load the client to your Roku device

To side-load the client:

youi-tv roku-client -l -s <Roku IP address> -u rokudev:<password>

The following code sample shows how to send a custom event:

import { NativeModules} from "react-native";
const Cloud = NativeModules.Cloud;
// ...
Cloud.invokeCommand("CUSTOM_COMMAND", { payload: "PAYLOAD" });

After you register the custom command, you can use the invokeCommand method from the Cloud API to send the new custom command. The invokeCommand method takes two parameters:

  • The name of the command
  • An object that includes any required arguments

Run the server

Run the server. See Building and Running a Roku App.

Custom command logs

You should see the following logs each time you invoke the custom command:

On the server:

2021-12-01 16:17:50.050 rokuApp[35151:3002180] I/ServerCommand:   Sending command: (CUSTOM_COMMAND) at sequence: 232739048798.

On the BrightScript Console:

[2021-12-02 00:19:01.888Z] processCommand: CUSTOM_COMMAND
[2021-12-02 00:19:01.888Z] command: CUSTOM_COMMAND, payload: PAYLOAD