Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Custom driver installation on the Remote

Starting with firmware v1.9.0, custom installation drivers can be installed on the Remote.

‼️ Custom integration drivers is a developer preview and not all features are implemented yet. See missing features.

Installation archive format

Integration driver archive requirements:

  • TAR GZip archive (either .tgz or .tar.gz file suffix) with a maximum size of 100 MB.
  • In the root of the archive, there must be a driver.json metadata file describing the custom integration driver.
  • The driver binary must be in the ./bin subdirectory.
    • Either a statically linked aarch64 executable named driver.
    • Or a Node.js file named driver.js.
  • All application files must be in one of the following subdirectories, other locations are not accessible at runtime:
    • ./bin: application binary folder.
    • ./config: optional configuration data. Path is accessible with UC_CONFIG_HOME environment variable.
    • ./data: optional application data. Path is accessible with UC_DATA_HOME environment variable.

Metadata file

The driver.json metadata file in the root of the archive describes the integration driver.

‼️ The data schema is not yet included in the Core-API, the full object is shown below. It is a reduced version of the IntegrationDriver object, without driver connection fields like driver_url, token etc.

{
  "driver_id": "foobar",
  "version": "1.0.0",
  "min_core_api": "0.20.0",
  "name": {
    "en": "Foobar special"
  },
  "icon": "custom:foobar.png",
  "description": {
    "en": "Control Foobar products",
    "de": "Steuere Foobar Produkte"
  },
  "features": [
    {
      "name": "auth.external_tokens",
      "required": true
    }
  ],
  "developer": {
    "name": "Unfolded Circle ApS",
    "email": "hello@unfoldedcircle.com",
    "url": "https://www.unfoldedcircle.com"
  },
  "home_page": "https://www.unfoldedcircle.com",
  "setup_data_schema": {
    "title": {
      "en": "Integration setup",
      "de": "Integrationssetup"
    },
    "settings": [
      {
        "id": "info",
        "label": {
          "en": "Setup process",
          "de": "Setup Fortschritt"
        },
        "field": {
          "label": {
            "value": {
              "en": "The integration will discover your Foobar products on your network. This process is automatic and can take a few minutes.",
              "de": "Diese Integration wird Foobar Produkte im Netzwerk finden. Dies ist ein Automatischer Prozess der ein paar Minuten dauern kann."
            }
          }
        }
      }
    ]
  },
  "release_date": "2024-07-21"
}
  • driver_id must be at least 5 characters long and start with a lower-case letter.
    • Only lower-case letters, digits and -, _ are allowed.
    • Common system identifiers are not allowed and will be rejected (e.g. daemon, backup, etc.).
    • The uc_ prefix should not be used. This is reserved for pre-installed integrations. Any custom integrations using this prefix might get removed during a future firmware update.
  • Multiple language fields are not required, but recommended.
    • If only a single language is provided, it should use the en key.
  • setup_data_schema is optional if the driver provides a setup flow.
    • This is the first screen of the setup, all further screens are dynamically provided by the driver.
  • ⚠️ min_core_api is not yet used.

Driver icon

A driver icon can either use a predefined icon or a custom icon. Predefined icons are prefixed with uc:, followed by the icon identifier in lowercase.

A custom driver icon can be installed automatically as a custom icon resource.

  1. In driver.json set the icon field to custom:$ICON_FILENAME
  2. Include $ICON_FILENAME in the root of the archive.

Example driver.json (other fields omitted for simplicity):

{
  "icon": "custom:foobar.png"
}

The icon file called foobar.png must be added to the root of the archive.

Icon requirements and restrictions:

  • Size must be 90x90 pixels.
  • Supported image formats: PNG and JPG.
  • Maximum size: 16 KB.

Installation archive example

Example of a Node.js based integration driver archive (contents of bin/node_modules are not shown for a clearer overview):

.
├── bin
│   ├── driver.js
│   ├── node_modules
│   │   ├── ...
│   │   ├── foobar
│   │   ├── uc-integration-api
│   │   ├── ws
│   │   └── ...
│   └── foobar.js
├── config
├── data
├── driver.json
└── foobar.png
  • If the driver doesn’t require any data or configuration files, the ./data and ./config directories can be omitted from the archive.
    • During the installation, the data and config directories are automatically created and can be accessed by the driver at runtime, no matter if they were provided in the installation archive or not.
  • The driver.json file in the root folder is automatically copied to the ./bin folder during installation if it is missing.
    • If the driver requires a different driver.json file, simply include a file in the ./bin folder.
      The one in the root folder won’t be copied anymore.

Restrictions

  • Maximum 10 custom integrations can be installed.
  • Only Node.js is supported beside a statically linked binary. Other runtimes are not supported at the moment.
    • Python integrations must pack the Python runtime into the archive.
  • The integration driver runs in a sandbox. Access to devices and the filesystem is restricted.
  • No symlinks are allowed. They are automatically removed during the installation.
  • Executable files are only allowed in the ./bin directory.
    • No other tools are provided in the runtime environment. E.g., there is no shell available and no other tools like cp or mv.

Missing features

  • Resource usage: provide memory and CPU usage per integration.
    • Only the overall resource usage can be monitored with GET /api/pub/status.

Runtime environment

The driver runs in a sandbox with limited access to the host system.

  • Environment variables for the Websocket integration-API server:
    • UC_INTEGRATION_INTERFACE: network interface to bind to.
    • UC_INTEGRATION_HTTP_PORT: port number.
  • The working directory is set to the binary directory.
  • The binary directory is read-only.
  • Node.js version: v22.22 (firmware release 2.9.2 and newer, v22.13 in firmware 2.8.2, v20.16 in firmware 1.9.3).
    • There are no pre-installed node modules.
    • An integration driver must include all required modules in the installation archive, including uc-integration-api (if used).
  • File access with relative paths between bin, config, and data is not possible.
    • Environment variables must be used to retrieve the full path of these directories.
      • UC_CONFIG_HOME and HOME: configuration directory.
      • UC_DATA_HOME: data directory.
    • The returned path may not be stored, it may change with future software updates.
  • Only the $UC_CONFIG_HOME and $UC_DATA_HOME directories are writeable and persisted between restarts.
    • The /tmp directory can be used for small temporary files.
      • This is a RAM file system and limited by the available free memory.
      • Temporary files are not persisted between restarts.
  • A dynamic user and group is allocated when the driver is started.
    • The user and group IDs may change between driver restarts.
    • Write access to the $UC_CONFIG_HOME, $UC_DATA_HOME and /tmp directories is ensured.

Resource restrictions

A single custom integration should not use more than 100 MB of memory and conserve CPU usage.

  • All custom integrations (max 10) share a common memory pool of 1 GB and are limited to 200% CPU usage (2 cores).
  • If the memory pool is exceeded, custom integrations will be terminated. Integrations using the most memory will be terminated first.
  • A single custom integration will be throttled if it uses more than 250 MB and terminated if it uses more than 350 MB.

Network

The integration runs on the same network environment as the Remote. There is no network bridge or firewall.

  • Binding ports below 1024 is not possible.

⚠️ A port-binding filter might be added in the future to prevent integrations to steal away ports of other integrations. The port range 8000-9200 and port 13333 are not allowed to be used!

Native integration drivers

TODO sysroot, available dynamic libraries

The Remote Two Cross Compile Toolchain can be used to cross-compile a native binary for the Remote.

  • The driver must be compiled as a static binary for libc.
  • Most dynamic libraries in the cross-compile sysroot are NOT available in the custom integration runtime environment!

Driver installation

The easiest way to manage custom integration drivers is to use the web-configurator.

Upload a new driver

In the web-configurator open the integration view and chose Add new, Install custom.

REST API

curl --location 'http://$IP/api/intg/install' \
--user 'web-configurator:$PIN' \
--form 'file=@"custom-intg.tar.gz"'

See REST Core API for more details.

Update driver

ℹ️ The driver update feature requires firmware release v2.9.3 or newer.

In the web-configurator open the integration view and chose Add new, Install custom and check the Update existing driver option.

REST API

curl --location 'http://$IP/api/intg/install?update=true' \
--user 'web-configurator:$PIN' \
--form 'file=@"custom-intg.tar.gz"'

Delete driver

The integration instance and driver can be deleted in the web-configurator within the main integration view. If there’s an active instance of the custom integration driver, the instance needs to be deleted first, then the driver.

REST API

To delete a custom integration, use the regular endpoints to delete an integration instance and driver. These are the same endpoints as for an external network integration driver:

  • Delete integration instance DELETE /api/intg/instances/:intgId.
  • Delete driver and installation files: DELETE /api/intg/drivers/:driverId.

Log access

Output to stdout and stderr are automatically stored with a timestamp and accessible as the other system logs:

  • Get log services: GET /api/system/logs/services.
  • Query logs: GET /api/system/logs?....

See REST Core API for more details.

Log files can also be downloaded in the web-configurator: Settings, Development: Logs

Web-app log viewer

ℹ️️ Available from firmware release v2.1.0

The Logdy web application is installed on the device to monitor integration driver log events in near real time.

  • URL: http://$IP/log/
  • This function is deactivated by default and the endpoint returns 503.
  • The log viewer must be started using the REST Core API with the /api/system/logs/web endpoint:
    • GET request returns the current state.
    • PUT request allows to start, stop or to permanently enable the log app. A password can be set to restrict access to the web app.
  • Available logs:
    • Remote core service
    • All pre-installed and custom integrations
    • Custom remote-ui service if installed
  • ❗️The log processing and logdy daemon require around 170 MB of the custom integration memory pool.

Using curl to start the app:

curl --request PUT 'http://$IP/api/system/logs/web' \
  --header 'Content-Type: application/json' \
  --user web-configurator:$PIN \
  --data '{"enabled": true}'

Start and permanently enable the app, even after a reboot:

curl --request PUT 'http://$IP/api/system/logs/web' \
  --header 'Content-Type: application/json' \
  --user web-configurator:$PIN \
  --data '{"enabled": true; "autostart": true}'

Recommendations

  • Node.js should be preferred for writing integration drivers.
  • If using Python:
    • Use PyInstaller to create a binary distribution.
    • Use the default one-folder bundle containing an executable (--onedir). Avoid the one-file bundled executable, since it will easily use an additional 100 MB of memory!
    • See Android TV integration or Apple TV integration as an example.
  • An integration driver should be limited to one process and not launch other processes.
  • Use ports above 10000 if the integration needs to create an IPv4 or IPv6 server socket (besides the required WebSocket server for the integration-API).
  • Preserve resources, use as little memory and CPU as possible.
  • Use stdout & stderr for logging.
  • Only use /tmp for small, temporary files since it is a RAM file system.

Example integrations

The following integration drivers create a custom integration installation archive during build with a GitHub action: