Skip to main content

Feature: Macropad Integration (Stream Dock M18)

Version: 0.1.0 Last Reviewed: 2026-02-15 Status: Draft

User Story

As a parent, I can press physical buttons on macropads placed around the house (nursery, changing table, etc.) to quickly log baby events and control smart home devices, so that tracking is effortless even at 3 AM with one hand.

Research Summary

Device: VSD Inside Stream Dock M18

Hardware Specifications (from official Python SDK docs + GitHub source):

  • 15 physical buttons (keys 1-15), arranged in a 5x3 grid
  • 64x64 pixel key icons — JPEG format, each button displays a custom image
  • 480x272 pixel touchscreen — full background image for dashboard display
  • RGB LED ring — 24 LEDs, full RGB color (0-255), brightness 0-100%, effects
  • USB HID communication — HIDAPI library, Vendor IDs 0x5500 / 0x5548
  • Multiple hardware revisions: M18, M18V2, M18V25, M18V3 (all SDK-supported)

Key Capabilities for Baby Tracking:

  • Button press/release events with callback support
  • Custom 64x64 icons per button (e.g., bottle, diaper, moon icons)
  • Full 480x272 dashboard on touchscreen (last feeding time, timer, etc.)
  • LED color feedback (green=success, red=error, pulse=sleep active)
  • Hot-plug detection via DeviceManager.listen()
  • Async callback support for event loop integration

Python SDK

Package: streamdock-sdk — installed from GitHub source (NOT available on PyPI)

Source: https://github.com/MiraboxSpace/StreamDock-Device-SDK/tree/main/Python-SDK

Core API:

MethodDescription
DeviceManager()Create manager, enumerate devices
device.open() / device.close()USB connection lifecycle
device.init()Wake screen, set brightness, clear icons
device.set_key_image(key, path)Set 64x64 JPEG icon on button (1-15)
device.set_touchscreen_image(path)Set 480x272 background
device.set_key_callback(fn)Sync callback: fn(device, key, state)
device.set_key_callback_async(fn)Async callback for asyncio
device.set_led_color(r, g, b)Set LED ring color
device.set_led_brightness(0-100)LED brightness
PILHelper.create_image(device, bg)Create blank key-sized image

Dependencies: Pillow, pyusb, pyudev (Linux), libhidapi

Raspberry Pi Zero 2 W

Recommended over original Pi Zero (quad-core ARM Cortex-A53 vs single-core ARM11).

  • OS: Raspberry Pi OS Lite (Bookworm, 64-bit) — headless, Python 3.11+
  • USB: Single micro-USB OTG port; needs adapter for Stream Dock
  • Power: 5V/3A recommended (Pi + USB device draw)
  • Auto-start: systemd service + Python venv

udev rules required for non-root USB access:

SUBSYSTEM=="usb", ATTR{idVendor}=="5500", MODE="0666"
SUBSYSTEM=="usb", ATTR{idVendor}=="5548", MODE="0666"

Home Assistant Integration

Approach: REST API (simplest, recommended for MVP)

requests.post(f"{HA_URL}/api/services/light/toggle",
headers={"Authorization": f"Bearer {HA_TOKEN}"},
json={"entity_id": "light.nursery"})

Future: WebSocket API for real-time state updates (button icons reflect light on/off state).

Architecture Decision: Standalone Repository

NOT part of Baby Basics monorepo. Rationale:

  1. Different runtime (Python on Pi vs Node.js + Swift)
  2. Different deployment model (flashed to Pi appliances vs server/App Store)
  3. Different release cadence (hardware integration evolves independently)
  4. Home Assistant integration is orthogonal to Baby Basics core
  5. Each macropad runs independently; config is per-device
  6. Simpler for potential open-sourcing

Proposed Repo Structure

baby-macropad/
pyproject.toml
src/baby_macropad/
main.py # Entry point
config.py # YAML config loader
device.py # StreamDock M18 wrapper
actions/
baby_basics.py # Baby Basics API client
home_assistant.py # HA REST API client
composite.py # Multi-action (log + dim light)
ui/
icons.py # Icon generation (Pillow)
dashboard.py # Touchscreen layout
feedback.py # LED + icon feedback
offline/
queue.py # SQLite offline buffer
sync.py # Background sync worker
web/
app.py # FastAPI config UI
config/
default.yaml # Default button layout
icons/ # Icon assets
tests/
scripts/
install.sh # Pi setup script

Configuration via YAML

device:
brightness: 80
led_idle_color: [30, 0, 60] # dim purple

baby_basics:
api_url: "https://baby.bretzfam.com/api"
token: "${BB_API_TOKEN}"
child_id: "uuid-here"

home_assistant:
url: "http://homeassistant.local:8123"
token: "${HA_TOKEN}"

buttons:
1:
label: "Breast L"
icon: "breast_left.png"
action: baby_basics.log_feeding
params:
type: breast
started_side: left
feedback:
success_led: [0, 255, 0]
2:
label: "Breast R"
action: baby_basics.log_feeding
params:
type: breast
started_side: right
3:
label: "Bottle"
action: baby_basics.log_feeding
params:
type: bottle
amount_ml: 120
4:
label: "Wet"
action: baby_basics.log_diaper
params:
type: wet
5:
label: "Dirty"
action: baby_basics.log_diaper
params:
type: dirty
6:
label: "Sleep"
action: baby_basics.toggle_sleep
7:
label: "Note"
action: baby_basics.log_note
params:
content: "Quick note logged"
# Home Assistant
11:
label: "Nursery Light"
action: home_assistant.toggle
params:
entity_id: "light.nursery"
12:
label: "Night Light"
action: home_assistant.toggle
params:
entity_id: "light.nightlight"
13:
label: "Fan"
action: home_assistant.toggle
params:
entity_id: "fan.nursery"
14:
label: "Sound Machine"
action: home_assistant.toggle
params:
entity_id: "media_player.sound_machine"
15:
label: "All Off"
action: home_assistant.scene
params:
entity_id: "scene.nursery_off"

Tech Stack

ComponentChoiceRationale
Python3.11+Pre-installed on Pi OS Bookworm, good async
Stream Dock SDKFrom GitHub sourceNo PyPI package; pin to git commit
Web frameworkFastAPIAsync-native, auto docs, Jinja2 templates
ASGI serverUvicornLightweight, works on Pi Zero 2 W
ConfigYAML + PydanticHuman-editable, validated at startup
Offline queueSQLite (stdlib)Zero dependency, reliable event buffer
HTTP clienthttpxAsync support, timeout handling
Image generationPillowRequired by StreamDock SDK already
Process managersystemdNative on Pi OS, restart on crash

Baby Basics API Endpoints Used

All endpoints under /children/:childId/:

ActionMethodEndpoint
Log feedingPOST/children/:id/feedings
Log diaperPOST/children/:id/diapers
Start sleepPOST/children/:id/sleeps
End sleepPATCH/children/:id/sleeps/:sleepId
Log notePOST/children/:id/notes
Get dashboardGET/children/:id/dashboard

Offline Buffering Strategy

  • SQLite queue on Pi: When API unreachable, store events with timestamp + payload
  • Retry on reconnect: Background thread polls, flushes queue on recovery
  • LED feedback: Green flash=success, yellow pulse=queued offline, red=persistent failure
  • Idempotency: Client-generated UUID per event prevents duplicates on retry
  • Queue size: 1000+ events; aggressive retry (exponential backoff 1s-60s)

MVP Scope

Phase 1: Core Baby Tracking

  • Stream Dock M18 device detection and initialization
  • YAML-based button configuration
  • Baby Basics API client (auth, POST feedings/diapers/sleeps)
  • Key icon rendering (Pillow, text + simple icons)
  • LED feedback on button press (green flash = success)
  • Touchscreen dashboard showing last event times
  • Offline SQLite queue with background sync
  • systemd service for auto-start on boot
  • Pi setup script (install.sh)

Phase 2: Home Assistant

  • HA REST API client (toggle lights, fans, scenes)
  • Button-to-entity mapping in YAML
  • State-aware icons (light ON = yellow, OFF = gray)

Phase 3: Web Config UI

  • FastAPI web app on Pi (port 8080)
  • View/edit button layout
  • Monitor event log and sync status

NOT in MVP

  • WebSocket HA state subscription (Post-MVP)
  • Multi-child support / page buttons (Post-MVP)
  • OTA update mechanism (Post-MVP)
  • Sleep timer display on touchscreen with elapsed time (Post-MVP)
  • Suggested feeding side from dashboard API (Post-MVP)
  • 3D-printed Pi Zero + M18 mounting bracket (Post-MVP)

Relevant Open Source Projects

ProjectStarsRelevance
basnijholt/home-assistant-streamdeck-yaml351Closest architecture model. YAML button-to-HA action mapping.
MiraboxSpace/StreamDock-Device-SDKOfficial SDK source (Python + C++)
tobimori/streamdeck-homeassistant82Elgato Stream Deck HA plugin. Service call patterns.
python-elgato-streamdeck~500+Gold-standard Python Stream Deck library. Architecture reference.
4dcu-be/MacroPad3Raspberry Pi powered macropad. Pi + USB integration.

Known Gotchas

  1. SDK maturity: Relatively new, maintained by one developer. Mix of Chinese/English docs. Small community — expect to read source code.
  2. No PyPI package: Must install from GitHub source. Pin to specific commit.
  3. Temp file pattern: set_key_image() saves temp files to disk. Use tmpfs mount on Pi to avoid SD card wear.
  4. Thread safety: DeviceManager is NOT thread-safe. Callbacks run in USB read thread — keep handlers fast, offload to queue.
  5. Pi Zero USB: Single OTG port needs adapter. Stream Dock may need powered USB hub if power draw is too high.
  6. 64x64 icon size: Very small. Use high-contrast simple icons, not text labels.
  7. Security: API tokens stored on Pi filesystem. Use chmod 600 on config. Keep Pi on trusted home network.
  8. Multiple macropads: Each Pi is independent. Consider shared provisioning script for fleet setup.
  9. ctypes native loading: SDK loads libhidapi via ctypes. ARM build ships with Pi OS apt but verify compatibility.

Boundaries

  • This spec covers the macropad appliance only (standalone Python app on Pi)
  • Baby Basics API and iOS app are separate — macropad is a client
  • Home Assistant setup/configuration is out of scope (assumes HA is already running)
  • Hardware purchasing and physical setup are out of scope