Skip to main content

Roku players and TVs (hc-roku)

Brings Roku streaming players and Roku TVs into homeCore over the External Control Protocol (ECP) — a plain-HTTP API every Roku serves on port 8060.

Each device registers as one media_player. Devices on the local subnet are found automatically over SSDP; nothing needs configuring beyond the broker connection.

Before you start

On each Roku, set:

Settings → System → Advanced system settings → Control by mobile apps → Network access = Default or Permissive

With this Disabled, ECP still answers status queries but rejects every keypress with HTTP 403 — the device appears in homeCore, reports what it is playing, and ignores all control. The plugin surfaces that 403 with the setting to change, but it is easier to set it first.

Published state

AttributeNotes
stateplaying | paused | stopped | idle | buffering | unavailable
on, power_modepower_mode is Roku's own: PowerOn, Ready, DisplayOff, Headless, PowerOff
source, app_id, app_name, app_type, app_versionActive channel or TV input; source is "Home" on the home screen
is_tv_inputTrue when the active "app" is an HDMI/AV/tuner input
media_title, media_descriptionLive-TV programme info — Roku reports no title for streaming apps
media_position, media_durationSeconds, with _ms variants alongside
media_is_live, media_error, media_formatFrom query/media-player
screensaver_active, screensaver_name
tv_channel, tv_channel_name, tv_channel_infoRoku TV tuned to the antenna input
available_sources, available_apps, available_inputs, available_tv_channelsRefreshed hourly
model_name, serial_number, software_version, is_tv, is_stick, supports_*Plus the full firmware dump under device_info

There is no volume attribute. ECP exposes VolumeUp/Down/Mute as key presses and reports no level back, so there is nothing to publish. Same reason there is no seek: media_position is read-only.

Commands

Send to homecore/devices/{id}/cmd, or PATCH /api/v1/devices/{id}/state. Both an action form and an attribute form work.

Attributeson (bool), source (channel/input name or id), state (playing / paused / stopped), tv_channel, mute, key, text.

GroupActions
Powerpower_on, power_off, power_toggle
Transportplay, pause, play_pause, stop, next, previous, instant_replay
Navigationhome, back, select, up, down, left, right, info, enter, backspace, find_remote
Volumevolume_up, volume_down, mute — all accept count
Live TVchannel_up, channel_down, tune (channel: "14.3")
Appslaunch_app (app, content_id, media_type, params), select_source, install_app, exit_app, app_state
Raw inputkey (key, count, hold_ms), key_hold, key_down, key_up, text (text, submit), send_input, search

play and pause are idempotent. Roku's Play is a single toggling key, so both check the last polled state and do nothing when the device is already where you asked for — which is what makes them safe in a rule. play_pause toggles unconditionally.

# Turn on and open Netflix
curl -X PATCH localhost:8080/api/v1/devices/roku_living_room/state \
-H "Authorization: Bearer $TOKEN" \
-d '{"on": true, "source": "Netflix"}'

# Deep-link to a title
curl -X PATCH localhost:8080/api/v1/devices/roku_living_room/state \
-H "Authorization: Bearer $TOKEN" \
-d '{"action":"launch_app","app":"Netflix","content_id":"80100172","media_type":"movie"}'

# Type into a search box and submit
curl -X PATCH localhost:8080/api/v1/devices/roku_living_room/state \
-H "Authorization: Bearer $TOKEN" \
-d '{"action":"text","text":"the expanse","submit":true}'

# Tune a Roku TV to 14.3
curl -X PATCH localhost:8080/api/v1/devices/roku_tv/state \
-H "Authorization: Bearer $TOKEN" \
-d '{"action":"tune","channel":"14.3"}'

Plugin actions

ActionPurpose
discover_devicesStreaming SSDP sweep; registers what it finds
list_devicesWhat the plugin manages, with address, serial, and reachability
refresh_catalogRe-read installed channels, inputs, and the TV lineup now
device_infoRaw query/device-info per device — the first diagnostic to reach for
send_commandRun one device command and get a real success/failure back
app_iconChannel icon as a data URI
forget_stale_devicesUnregister discovered devices that no longer answer (admin)

Setup

Install it from the registry — Plugins → Add in the web UI — and set the broker connection. Rokus on the subnet register themselves within a few seconds; run discover_devices to sweep on demand.

[[devices]] in the plugin's config is optional. Use it to pin a device id, to reach a Roku across a VLAN, or when running with discovery_enabled = false.

Notes on the protocol

  • Discovery is link-local. SSDP multicast does not cross VLANs, and does not cross a Docker bridge network. Use manual_hosts — or host networking — for anything the multicast cannot reach. ECP itself is ordinary unicast HTTP and works regardless.
  • Identity survives DHCP. Discovered devices are keyed by serial number and stored in the plugin's durable state, so a Roku that changes address keeps its homeCore id, name, room, and rules.
  • Powering on a TV that is fully off needs Wake-on-LAN — nothing is listening for PowerOn. The plugin caches the MAC while the device is reachable and sends a magic packet when it is not.
  • The search endpoint was sunset in Roku OS 12.0 and is a no-op on current firmware. exit_app and app_state are developer-mode only.