Skip to main content
Local Abilities are a specialized Ability type for running DevKit-side code from an OpenHome Ability. Unlike other Ability types, which operate only within the standard Ability runtime, Local Abilities can use the DevKit hardware, system resources, and the Python environment installed on the device. This includes Python imports that are restricted in the standard runtime, file system operations, shell commands, hardware access such as GPIO pins, sensors, LEDs, and connected peripherals, and system-level data such as CPU, memory, temperature, and network state. Use Local Abilities for IoT projects, custom hardware integrations, voice-controlled physical devices, device telemetry, long-running on-device tasks, and any use case that requires direct interaction with the DevKit or capabilities beyond the standard Ability runtime.
Local Abilities only run on actual OpenHome DevKit hardware. They do not run in the web Live Editor’s simulated environment.

How It Works

A Local Ability is split between the standard Ability runtime and DevKit-side execution. main.py handles the Agent flow, while devkit_functions.py runs hardware, system, and device-level code on the OpenHome DevKit.

File Structure

The DevKit-side file must be named exactly devkit_functions.py. No other filename will be picked up by the platform.
Packages listed in requirements.txt are installed for devkit_functions.py on the OpenHome DevKit. They are not available in the standard Ability runtime where main.py runs.

Calling DevKit Functions

Use send_devkit_capability_action() in main.py to run a registered function from devkit_functions.py.

devkit_functions.py Execution Flow

devkit_functions.py runs on the OpenHome DevKit as a Python script. Functions that should be callable from main.py must be registered in FUNCTION_REGISTRY, and the function_name passed from main.py must match one of those registry keys. devkit_functions.py should include a Python main guard: if __name__ == "__main__". The main guard reads the requested function name and arguments, then runs the matching registered function. Values in args are passed to the DevKit-side function as strings. Cast them inside devkit_functions.py when the function requires a specific type, such as an integer, boolean, or JSON object. Use print() for output that should be returned to main.py; standard output is captured in result["output"]. Python return values are not captured by send_devkit_capability_action(). Use web_logger for diagnostics. These logs appear in the DevKit section of the Ability Live Editor and are not returned to main.py.

Response Shape

send_devkit_capability_action() returns an object with the execution status, captured output, and request metadata.
output contains the standard output produced during execution. If the function does not print anything, output is None. error contains the error message when execution fails. Otherwise, it is None. Logs written with web_logger are separate from the returned object. They appear in the DevKit section of the Ability Live Editor logs and are useful for debugging DevKit-side execution.

Example: Wi-Fi Status

This example reads the DevKit’s current Wi-Fi connection and speaks it back to the user. devkit_functions.py — runs on the DevKit:
main.py — runs in the standard Ability runtime:

Calling Functions from Another Local Ability

main.py can also call functions from another installed Local Ability’s devkit_functions.py. This is useful when one Local Ability exposes reusable DevKit-side functions that another Ability needs to use. capability_name is only needed for cross-Ability calls. When it is omitted, the call uses the current Ability’s devkit_functions.py.
To find the name of an installed Ability to use as capability_name, see Installed Abilities later in this document.

Local Abilities in the Live Editor

Select the Local Category

To create a Local Ability, select Local from the Ability categories and choose a template. If you upload a custom Ability, the project must include devkit_functions.py and requirements.txt.

Advanced DevKit Controls

If your DevKit is online and connected, the Advanced DevKit Controls toggle appears in the Ability Editor. Enable it to expand the Advanced DevKit Controls section. Once Advanced DevKit Controls are enabled, scroll down and you will see the Advanced DevKit Controls section. From here you can sync your Ability to the DevKit, restart the Agent, and view the DevKit connection status.

Advanced DevKit Controls button

Advanced DevKit Controls and Sync Abilities button

Sync Local Abilities with the DevKit

When the DevKit is online and connected, changes saved in the Live Editor are synced to the DevKit automatically. On save:
  • devkit_functions.py or requirements.txt changes are pushed to the DevKit without restarting the Agent. If requirements.txt changed, new dependencies are installed on the DevKit.
  • main.py changes are saved to the OpenHome platform, synced with the DevKit sandbox, and the Agent restarts on the DevKit so the latest Ability code is used.
When editing main.py, save after completing the intended change. Each save can restart the Agent on the DevKit while the DevKit is connected. If the DevKit was offline while you updated a Local Ability:
  • main.py changes sync when the DevKit reconnects.
  • devkit_functions.py or requirements.txt changes should be synced before testing. After the DevKit reconnects, click Sync Abilities from Advanced DevKit Controls to apply the latest changes.
You can also sync from Advanced DevKit Controls in the Live Editor, or from the OpenHome - Voice AI Devkit App dashboard using the Sync Abilities button Sync Abilities Button.

Logging on the DevKit

Use the DevKit logger inside devkit_functions.py to debug on-device behavior. Messages written with this logger appear in the DevKit section of the Ability Editor logs.
To view the logs, open the DevKit section inside the Ability Editor logs after triggering the Ability on the DevKit.

DevKit logs section in the Ability Editor

Installed Abilities

To use functions from another Ability’s devkit_functions.py, you need that Ability’s name to pass in the capability_name parameter. To find it, click the Quick Reference Installed Abilities button in the top-left corner of the Ability Editor.

Quick Reference Installed Abilities button

This opens the installed Local Abilities list. Copy the name of the Local Ability that contains the target devkit_functions.py file and pass it in the capability_name parameter.

Example: DevKit Stats

This is a voice-controlled DevKit telemetry reporter. Users say something like “check cpu” or “how hot is the devkit” and the DevKit reads its system stats and speaks them back.

Trigger words

This example can be triggered with phrases like:
  • devkit info
  • system info
  • how long has my devkit been running

requirements.txt

No third-party packages are required for this example — all stat checks use Python’s standard library and standard Linux interfaces (/proc, /sys, and shell commands like iwgetid, df). For other Local Abilities that need hardware libraries, list them here. Some common examples:
Only the packages you actually import in devkit_functions.py need to go here — they get installed on the DevKit side when you sync.

main.py — standard Ability runtime

devkit_functions.py — DevKit-side telemetry

Interaction Flow

1

Trigger

The user starts the Ability with a trigger phrase such as “devkit stats” or “check cpu”.
2

Route

main.py keeps the voice flow in the standard Ability runtime and uses the LLM as a strict router from natural language to a registered DevKit telemetry function.
3

Execute

main.py calls send_devkit_capability_action() with the selected function name, arguments, and timeout. The matching function runs on the OpenHome DevKit from devkit_functions.py.
4

Return output

devkit_functions.py reads the requested device data, logs diagnostics with web_logger, and prints a structured JSON payload. The printed payload is captured in result["output"].
5

Respond

main.py parses result["output"], reads spoken_response, and speaks the result. The structured data field remains available for richer logic.
6

Continue or exit

The Ability prompts for another stat or exits cleanly. On exit, main.py calls resume_normal_flow() so the Agent returns to its normal flow.

Best practices

Clean separation makes both sides easier to debug. Keep devkit_functions.py focused on the hardware work.
Hardware calls can block. A 5–10 second timeout is typical for lightweight actions; bump to 30 or more for long-running effects or captures.
Use the DevKit logger web_logger for debugging and inspect messages in the DevKit logs section inside the Ability Editor.
Packages listed there are installed for devkit_functions.py. They are not available in the sandboxed runtime where main.py runs.
Not every DevKit has every peripheral. Wrap hardware initialization in try/except and log an informative error instead of crashing — your Ability can still speak a helpful message to the user.

See also