main.py can execute functions defined in devkit_functions.py on the OpenHome DevKit hardware, outside of the sandbox environment.
Local Abilities only run on actual OpenHome DevKit hardware. They do not run in the web Live Editor’s simulated environment.
What it unlocks
Running code throughdevkit_functions.py lets your Ability use features that are not available in the normal sandboxed runtime.
- Restricted Python libraries — use libraries that are not available inside sandboxed
main.py - Direct hardware access — control LEDs, GPIO pins, cameras, and connected devices
- Shell commands — run device-side commands from your DevKit functions
- Device-level data — read hardware state, temperature, memory, network details, and similar system data
- Long-running device tasks — run background effects, child processes, or other on-device workflows
How it works
Local Abilities are split across two sides:main.pyruns in the normal Ability runtime and handles the voice flow, prompts, conversation, and overall user experiencedevkit_functions.pyruns on the OpenHome DevKit and contains the on-device functions you want to execute
devkit_functions.py, then call them from main.py. The function runs on the DevKit and the result is returned to main.py.
In practice, keep the voice and conversation flow in main.py, and move hardware access, restricted libraries, and device-side operations into devkit_functions.py. The third file, requirements.txt, lists the packages that get installed on the DevKit side for devkit_functions.py to use.
Running functions on the DevKit from main.py
send_devkit_capability_action() is a CapabilityWorker method. Use it in main.py to:
- Call a function defined in
devkit_functions.pyand run it on the OpenHome DevKit - Pass arguments to that function
- Set how long to wait for the response
| Parameter | Description |
|---|---|
function_name | Name of the function to run from devkit_functions.py |
args | List of arguments passed to that function |
timeout | Number of seconds to wait before timing out |
capability_name | Optional. Use this only when you want to call a function from another Ability’s devkit_functions.py |
main.py from devkit_functions.py:
devkit_functions.py by passing that Ability’s name in the optional capability_name parameter.
Calling a function in main.py from another Ability’s devkit_functions.py:
capability_name, see Installed Abilities in the Local Abilities section below.
Local Abilities in Live Editor
Selecting the Local category
See Ability Types for the full breakdown. Local is one of the four Ability categories. To create a Local Ability, select Local Ability and choose a template from the templates. If you choose to upload a custom Ability, make sure it includesdevkit_functions.py and requirements.txt.

File structure
Local Abilities use three files that matter:| File | What goes here |
|---|---|
main.py | Voice flow, prompting, conversation logic, and calls to DevKit-side functions |
devkit_functions.py | Functions that run on the OpenHome DevKit and perform hardware or device-side work |
requirements.txt | Dependencies used by devkit_functions.py on the OpenHome DevKit side |
requirements.txt are installed for devkit_functions.py. They are not available in the sandboxed runtime where main.py runs.
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.

Syncing Local Abilities with the DevKit
Changes made in the Live Editor are applied to the DevKit when you save or sync them. Automatic sync on save PressCtrl + S or click the Save button in the Live Editor.
devkit_functions.pyorrequirements.txt— the file is pushed to the DevKit and any new dependencies inrequirements.txtare installed. You can see the sync status in the Advanced DevKit Controls section while the save is in progress.

main.py— the code is saved to the cloud and synced with the DevKit sandbox, and the Agent is restarted on the DevKit. After that, you can test the latest version of your Ability.
devkit_functions.pyorrequirements.txt— click Sync Abilities. Once synced, the latest DevKit-side code is live on the DevKit and any new dependencies are installed.main.py— click Sync Abilities, then click Restart Agent. This ensures the Agent is running the latestmain.pybefore you continue testing.
Logging on the DevKit
Use the DevKit logger insidedevkit_functions.py to debug on-device behavior. Messages written with this logger appear in the DevKit section of the Ability Editor logs.

Installed Abilities
To use functions from another Ability’sdevkit_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.

devkit_functions.py you want to call and pass it in the capability_name parameter.

Example: DevKit LED controller
This is a voice-controlled LED controller. Users say something like “make the ring pulse blue” and the DevKit’s NeoPixel ring responds.Trigger words
This example can be triggered with phrases like:control the lightslights controllights controller
requirements.txt
devkit_functions.py but are not installed in the sandboxed runtime where main.py runs.
main.py — the sandboxed voice flow
devkit_functions.py — the on-device hardware side
The interaction flow
LLM converts natural language to a function call
The user says “make it red” → LLM returns
{"function_name": "neopixel_solid", "args": ["ff0000", "180"], "spoken_response": "Making the ring red."}.main.py dispatches to the OpenHome DevKit
await self.capability_worker.send_devkit_capability_action("neopixel_solid", ["ff0000", "180"], 8)devkit_functions.py executes on the DevKit
The
neopixel_solid function runs on the DevKit, writes to the GPIO-connected LED strip, and returns the result.Result returns to main.py
main.py receives {"success": true} and speaks the confirmation to the user.Best practices
Put voice logic in main.py, hardware logic in devkit_functions.py
Put voice logic in main.py, hardware logic in devkit_functions.py
Clean separation makes both sides easier to debug. Keep
devkit_functions.py focused: do the hardware work and return the result.Set sensible timeouts
Set sensible timeouts
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 DevKit logs to debug on-device code
Use DevKit logs to debug on-device code
Use the DevKit logger
web_logger for debugging and inspect messages in the DevKit logs section inside the Ability Editor.requirements.txt is for the DevKit side only
requirements.txt is for the DevKit side only
Packages listed there are installed for
devkit_functions.py. They are not available in the sandboxed runtime where main.py runs.Handle missing hardware gracefully
Handle missing hardware gracefully
Not every DevKit has every peripheral. Wrap hardware initialization in
try/except and return an informative error instead of crashing — your Ability can still speak a helpful message to the user.See also
- Ability Types — when Local is the right choice vs. Skill, Brain, or Background Daemon
- Background Abilities — for always-on monitoring that doesn’t need hardware access
- SDK Reference — full method catalog
- Voice-First Best Practices — the UX rules that apply to any Ability, including Local

