Skip to content

ckamil/streamdeck

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

15 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

StreamDeck — wireless ESP32 control surface

An open-source Stream Deck / Bitfocus Companion alternative built on a 5" ESP32-S3 touch display + 2 rotary encoders. After first flash the device only needs USB-C power; everything else (config, firmware updates, button artwork) flows over WiFi from a companion agent running on your computer.

Screenshots

On-deck UI (800×480 touchscreen)

Home page OBS page Settings
home obs settings

Web configurator (http://localhost:8088)

Dashboard Deck editor Firmware
dashboard deck editor firmware

Deck screenshots are captured live via POST /api/devices/{id}/screenshot — the agent sends a WS request, the deck dumps its LVGL framebuffer (raw RGB565) over HTTP, the agent converts to PNG.

┌─────────────────┐       WiFi WS         ┌─────────────────────┐
│ ESP32-S3 deck   │ ◄──────────────────►  │ streamdeck-agent    │
│ • 800×480 LVGL  │  config, frames,      │ • web configurator  │
│ • 2 EC11 encs   │  stats, events        │ • plugin modules    │
│ • WiFi/BT5 LE   │                       │ • OBS/WLED/HTTP/MQTT│
└─────────────────┘                       └─────────────────────┘
                                                     │
                                          ┌──────────┴──────────┐
                                          │ Browser-based UI    │
                                          │ http://localhost:8088│
                                          └─────────────────────┘

Features

  • Bitfocus Companion-style modules — actions + feedbacks per integration; live state pushes to button visuals
  • Stream Deck-style multi-state buttons — toggle on/off with different icons and labels
  • Multi-action sequences — one press → many ordered actions, with delays
  • Animated icons — GIF or sprite sheets cycled at source FPS on device
  • Web configurator — drag-drop deck editor, button editor with 4 tabs (Appearance / Icon / Actions / States), icon library
  • System tray — runs in tray on Linux/Mac/Windows; auto-start at login
  • WiFi provisioning — captive portal on first boot; no cable after that
  • OTA updates — agent serves binaries; device pulls and verifies SHA-256
  • Cross-platform agent — Linux, macOS, Windows, RPi (one Python codebase)
  • Pluggable architecture — drop a Python file into modules/ to add an integration

Built-in modules

Module Actions Feedbacks
system hotkey, type_text, volume_set/delta, toggle_mute, run, open_url volume_current, is_muted
obs switch_scene, toggle_record/stream/mic, set_source_visible is_recording, is_streaming, is_mic_muted, current_scene
wled set_power, toggle, set_brightness, adjust_brightness, set_preset, set_color is_on, brightness, current_preset
http request, get, post_json
multi_action run_macro, run_sequence

Adding new modules: subclass Module, decorate methods with @action / @feedback. Auto-discovered, surfaced in the web UI without code changes.

Hardware

  • Viewe UEDX80480050E-WB-A — 5" 800×480 RGB IPS, ESP32-S3-WROOM-1 N16R8
  • 2 × EC11 rotary encoders (any 5- or 7-pin variant)
  • 1 × PCF8575 I²C GPIO expander (~10 zł)
  • Pull-ups, debounce caps, wires (BOM in docs/soldering.md)

Quickstart

1. Solder the encoders

Follow docs/soldering.md. Verify with the bring-up sketch in firmware/bringup/.

2. Install the agent

Linux / macOS:

curl -sSf https://raw.githubusercontent.com/kamil/streamdeck/main/install/install.sh | sh

Windows (PowerShell):

iwr -useb https://raw.githubusercontent.com/kamil/streamdeck/main/install/install.ps1 | iex

Manual (any OS with Python 3.11+):

pipx install streamdeck-agent

3. Configure

Open http://localhost:8088. Register a device, copy the auth token.

4. Flash main firmware

cd firmware && pio run -t upload

5. Provision the device

After first boot the deck creates a StreamDeck-XXXX WiFi access point. Connect with your phone — captive portal appears. Enter:

  • Your WiFi SSID + password
  • The agent's IP (e.g. 192.168.1.10) and port (8088)
  • Device ID + auth token from step 3

The deck reboots, joins your WiFi, pairs with the agent. Done — the device is now wireless and configurable from the web UI.

Repo layout

agent/                    Python agent (FastAPI + WS + tray + modules)
  streamdeck_agent/
    config/               Pydantic config models + JSON store
    core/                 Server, render, dispatcher, auth, firmware, stats
    modules/              system, obs, wled, http, multi_action
    audio/                Linux/Windows/macOS volume backends
    tray/                 pystray-based tray icon + state machine
    proto/                WS message types
  tests/{unit,integration}/

firmware/                 ESP32 firmware
  lib/                    Pure-C++ logic libs (host-testable)
    encoder/              Quadrature decoder + multi-encoder
    ui_state/             Profile/page/button model + touch translation
    proto/                WS protocol encode/decode (matches Python)
    provisioning/         Captive portal state machine + form parsing
    ota/                  Semver compare + URL building + SHA-256
  src/                    Hardware-only glue (LVGL, WiFi, OTA)
    ui/                   LVGL bridge (status bar, grid, animations)
    net/                  WiFi provisioning portal, OTA fetcher
    main.cpp              Boot + dispatch
  bringup/                Standalone sketch for hardware bring-up
  tests/                  Python ↔ C++ live round-trip
  CMakeLists.txt          Native (host) test build
  platformio.ini          Hardware build (ESP32-S3)

web/                      Svelte configurator
  src/{pages,components,lib}/

install/                  Cross-platform installers + service files
docs/                     Soldering, architecture, etc.

Tests

make test          # everything: agent + firmware native + interop + web type-check
make test-agent    # 318+ pytest cases
make test-firmware # 6 doctest binaries
make test-interop  # Python ↔ C++ live round-trip

CI runs all of these on Linux/macOS/Windows + Python 3.11/3.12/3.13. See .github/workflows/ci.yml.

Architecture highlights

  • Single source of truth for the protocol: pydantic schema in agent/streamdeck_agent/proto/messages.py. The firmware encoder/decoder in firmware/lib/proto/ is verified against it by a live Python ↔ C++ round-trip in CI — no schema drift.
  • All logic is host-testable. Firmware components (quadrature, OTA semver, provisioning state machine, UI model) are pure C++ with no Arduino dependency. Hardware-only files in src/ are thin shims.
  • Server-side rendering of button bitmaps. Device gets gradient text + animations with no on-device compositing — same trick Bitfocus uses.
  • Modules are isolated. Each plugin gets a dict of settings, exposes actions + feedbacks, never reaches into the registry. Pluggable via Python entry points (planned) or drop-in files (today).

License

MIT — do whatever, just don't blame me if your encoder catches fire.

About

Wireless StreamDeck — Viewe ESP32-S3 firmware + Python companion agent + Svelte web configurator

Resources

Stars

Watchers

Forks

Packages

 
 
 

Contributors