New `camera` module in modules.example.yaml (and the embedded copy) with four functions: `set` (any control=value list, incl. preset/colour buttons), `toggle` (flip between two values), `adjust` (step a numeric control by a delta, clamped to the min/max parsed from `cameractrls -l`, with optional `extra` controls applied first), and `is` (poll helper, exit 0 on match). Device comes from CAMERA_DEVICE in .env (default /dev/video0) or a per-key `device` param. cameractrls exits 0 for unknown controls, so polls must use `is`; stderr is discarded to drop Python deprecation warnings. README: Camera module section with setup, function table, a folder example, and the oksvg viewBox-offset centring gotcha for SVG icons. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017iodiNMYL9iuT7f6WCe5iv
273 lines
14 KiB
YAML
273 lines
14 KiB
YAML
# Example modules.yaml — copy to ~/.config/streamdeck-go/modules.yaml
|
|
#
|
|
# Required Slack token scopes: users.profile:write, users:write, dnd:write
|
|
# Export your token: export SLACK_TOKEN="xoxp-..."
|
|
|
|
modules:
|
|
slack:
|
|
set_status:
|
|
params:
|
|
emoji: ":speech_balloon:"
|
|
text: "In a meeting"
|
|
expiry: "1h"
|
|
exec: |
|
|
curl -s -X POST https://slack.com/api/users.profile.set \
|
|
-H "Authorization: Bearer {{env "SLACK_TOKEN"}}" \
|
|
-H "Content-Type: application/json; charset=utf-8" \
|
|
-d '{"profile":{"status_emoji":"{{.emoji}}","status_text":"{{.text}}","status_expiration":{{expiry .expiry}}}}'
|
|
|
|
clear_status:
|
|
exec: |
|
|
curl -s -X POST https://slack.com/api/users.profile.set \
|
|
-H "Authorization: Bearer {{env "SLACK_TOKEN"}}" \
|
|
-H "Content-Type: application/json; charset=utf-8" \
|
|
-d '{"profile":{"status_emoji":"","status_text":"","status_expiration":0}}'
|
|
|
|
set_presence:
|
|
params:
|
|
presence: "away"
|
|
exec: |
|
|
curl -s -X POST https://slack.com/api/users.setPresence \
|
|
-H "Authorization: Bearer {{env "SLACK_TOKEN"}}" \
|
|
-H "Content-Type: application/json; charset=utf-8" \
|
|
-d '{"presence":"{{.presence}}"}'
|
|
|
|
snooze:
|
|
params:
|
|
minutes: "60"
|
|
exec: |
|
|
curl -s -X POST https://slack.com/api/dnd.setSnooze \
|
|
-H "Authorization: Bearer {{env "SLACK_TOKEN"}}" \
|
|
-H "Content-Type: application/json; charset=utf-8" \
|
|
-d '{"num_minutes":{{.minutes}}}'
|
|
|
|
go_offline:
|
|
exec: |
|
|
curl -s -X POST https://slack.com/api/users.setPresence \
|
|
-H "Authorization: Bearer {{env "SLACK_TOKEN"}}" \
|
|
-H "Content-Type: application/json; charset=utf-8" \
|
|
-d '{"presence":"away"}' && \
|
|
curl -s -X POST https://slack.com/api/users.profile.set \
|
|
-H "Authorization: Bearer {{env "SLACK_TOKEN"}}" \
|
|
-H "Content-Type: application/json; charset=utf-8" \
|
|
-d '{"profile":{"status_emoji":":dumpsterfire:","status_text":"Offline","status_expiration":0}}'
|
|
|
|
end_snooze:
|
|
exec: |
|
|
curl -s -X POST https://slack.com/api/dnd.endSnooze \
|
|
-H "Authorization: Bearer {{env "SLACK_TOKEN"}}"
|
|
|
|
# Pomodoro / focus timer — the `flow` CLI.
|
|
#
|
|
# Absolute path required (minimal service PATH). Apple Silicon Homebrew installs
|
|
# live at /opt/homebrew/bin/flow — set that in ~/.config/streamdeck-go/.env:
|
|
# FLOW_CMD=/opt/homebrew/bin/flow
|
|
#
|
|
# Control verbs (start/pause/resume/toggle/skip/stop/reset/gui) run on key press.
|
|
# `status` prints one line like `state=running phase=work remaining=24:12` for
|
|
# icon-swap poll blocks (match: "state=running").
|
|
#
|
|
# The LIVE countdown label uses a key's `text_command` field directly (not a
|
|
# module), e.g. text_command: "/usr/local/bin/flow status --short" which prints
|
|
# a two-line label like WORK\n24:12 (an actual newline between the lines).
|
|
flow:
|
|
start: { exec: '{{envDefault "FLOW_CMD" "/usr/local/bin/flow"}} start' }
|
|
pause: { exec: '{{envDefault "FLOW_CMD" "/usr/local/bin/flow"}} pause' }
|
|
resume: { exec: '{{envDefault "FLOW_CMD" "/usr/local/bin/flow"}} resume' }
|
|
toggle: { exec: '{{envDefault "FLOW_CMD" "/usr/local/bin/flow"}} toggle' }
|
|
skip: { exec: '{{envDefault "FLOW_CMD" "/usr/local/bin/flow"}} skip' }
|
|
stop: { exec: '{{envDefault "FLOW_CMD" "/usr/local/bin/flow"}} stop' }
|
|
reset: { exec: '{{envDefault "FLOW_CMD" "/usr/local/bin/flow"}} reset' }
|
|
gui: { exec: '{{envDefault "FLOW_CMD" "/usr/local/bin/flow"}} gui' }
|
|
status: { exec: '{{envDefault "FLOW_CMD" "/usr/local/bin/flow"}} status' }
|
|
|
|
# OBS Studio — media player, streaming, and scene/transition control via obs-cmd
|
|
#
|
|
# Requires: obs-cmd (https://github.com/grigio/obs-cmd)
|
|
# macOS (Intel): brew install grigio/obs-cmd/obs-cmd → /usr/local/bin/obs-cmd
|
|
# macOS (Apple Silicon): brew install grigio/obs-cmd/obs-cmd → /opt/homebrew/bin/obs-cmd
|
|
# Linux: cargo install obs-cmd → ~/.cargo/bin/obs-cmd
|
|
# Linux (binary): download from GitHub releases, e.g. /usr/local/bin/obs-cmd
|
|
#
|
|
# OBS WebSocket must be enabled: Tools → WebSocket Server Settings (on by default in OBS 28+)
|
|
#
|
|
# Add to ~/.config/streamdeck-go/.env:
|
|
# OBS_WEBSOCKET_PASSWORD=your-password
|
|
# OBS_HOST=localhost (optional, default: localhost)
|
|
# OBS_PORT=4455 (optional, default: 4455)
|
|
# OBS_CMD=/opt/homebrew/bin/obs-cmd (optional, default: /usr/local/bin/obs-cmd)
|
|
# set this on Apple Silicon or Linux cargo installs
|
|
obs:
|
|
play:
|
|
params:
|
|
source: "Media Source"
|
|
exec: |
|
|
{{envDefault "OBS_CMD" "/usr/local/bin/obs-cmd"}} --websocket obsws://{{envDefault "OBS_HOST" "localhost"}}:{{envDefault "OBS_PORT" "4455"}}/{{env "OBS_WEBSOCKET_PASSWORD"}} media-input play "{{.source}}"
|
|
|
|
pause:
|
|
params:
|
|
source: "Media Source"
|
|
exec: |
|
|
{{envDefault "OBS_CMD" "/usr/local/bin/obs-cmd"}} --websocket obsws://{{envDefault "OBS_HOST" "localhost"}}:{{envDefault "OBS_PORT" "4455"}}/{{env "OBS_WEBSOCKET_PASSWORD"}} media-input pause "{{.source}}"
|
|
|
|
stop:
|
|
params:
|
|
source: "Media Source"
|
|
exec: |
|
|
{{envDefault "OBS_CMD" "/usr/local/bin/obs-cmd"}} --websocket obsws://{{envDefault "OBS_HOST" "localhost"}}:{{envDefault "OBS_PORT" "4455"}}/{{env "OBS_WEBSOCKET_PASSWORD"}} media-input stop "{{.source}}"
|
|
|
|
restart:
|
|
params:
|
|
source: "Media Source"
|
|
exec: |
|
|
{{envDefault "OBS_CMD" "/usr/local/bin/obs-cmd"}} --websocket obsws://{{envDefault "OBS_HOST" "localhost"}}:{{envDefault "OBS_PORT" "4455"}}/{{env "OBS_WEBSOCKET_PASSWORD"}} media-input restart "{{.source}}"
|
|
|
|
toggle_record:
|
|
exec: |
|
|
{{envDefault "OBS_CMD" "/usr/local/bin/obs-cmd"}} --websocket obsws://{{envDefault "OBS_HOST" "localhost"}}:{{envDefault "OBS_PORT" "4455"}}/{{env "OBS_WEBSOCKET_PASSWORD"}} recording toggle
|
|
|
|
toggle_record_pause:
|
|
exec: |
|
|
{{envDefault "OBS_CMD" "/usr/local/bin/obs-cmd"}} --websocket obsws://{{envDefault "OBS_HOST" "localhost"}}:{{envDefault "OBS_PORT" "4455"}}/{{env "OBS_WEBSOCKET_PASSWORD"}} recording toggle-pause
|
|
|
|
is_recording_paused:
|
|
exec: |
|
|
{{envDefault "OBS_CMD" "/usr/local/bin/obs-cmd"}} --websocket obsws://{{envDefault "OBS_HOST" "localhost"}}:{{envDefault "OBS_PORT" "4455"}}/{{env "OBS_WEBSOCKET_PASSWORD"}} recording status
|
|
|
|
toggle_stream:
|
|
exec: |
|
|
{{envDefault "OBS_CMD" "/usr/local/bin/obs-cmd"}} --websocket obsws://{{envDefault "OBS_HOST" "localhost"}}:{{envDefault "OBS_PORT" "4455"}}/{{env "OBS_WEBSOCKET_PASSWORD"}} streaming toggle
|
|
|
|
scene_switch:
|
|
params:
|
|
scene: "Scene 1"
|
|
exec: |
|
|
{{envDefault "OBS_CMD" "/usr/local/bin/obs-cmd"}} --websocket obsws://{{envDefault "OBS_HOST" "localhost"}}:{{envDefault "OBS_PORT" "4455"}}/{{env "OBS_WEBSOCKET_PASSWORD"}} scene switch "{{.scene}}"
|
|
|
|
toggle_mute:
|
|
params:
|
|
source: "Mic/Aux"
|
|
exec: |
|
|
{{envDefault "OBS_CMD" "/usr/local/bin/obs-cmd"}} --websocket obsws://{{envDefault "OBS_HOST" "localhost"}}:{{envDefault "OBS_PORT" "4455"}}/{{env "OBS_WEBSOCKET_PASSWORD"}} input toggle-mute "{{.source}}"
|
|
|
|
is_recording:
|
|
exec: |
|
|
{{envDefault "OBS_CMD" "/usr/local/bin/obs-cmd"}} --websocket obsws://{{envDefault "OBS_HOST" "localhost"}}:{{envDefault "OBS_PORT" "4455"}}/{{env "OBS_WEBSOCKET_PASSWORD"}} recording status
|
|
|
|
is_streaming:
|
|
exec: |
|
|
{{envDefault "OBS_CMD" "/usr/local/bin/obs-cmd"}} --websocket obsws://{{envDefault "OBS_HOST" "localhost"}}:{{envDefault "OBS_PORT" "4455"}}/{{env "OBS_WEBSOCKET_PASSWORD"}} streaming status
|
|
|
|
# Change the active transition type. Common values: Fade, Cut, Slide, Swipe, Stinger.
|
|
# Must match a transition that exists in your OBS profile (see Scene Transitions panel).
|
|
set_transition:
|
|
params:
|
|
transition: "Fade"
|
|
exec: |
|
|
{{envDefault "OBS_CMD" "/usr/local/bin/obs-cmd"}} --websocket obsws://{{envDefault "OBS_HOST" "localhost"}}:{{envDefault "OBS_PORT" "4455"}}/{{env "OBS_WEBSOCKET_PASSWORD"}} scene transition-set "{{.transition}}"
|
|
|
|
# Set the active transition duration in milliseconds.
|
|
set_transition_duration:
|
|
params:
|
|
duration: "300"
|
|
exec: |
|
|
{{envDefault "OBS_CMD" "/usr/local/bin/obs-cmd"}} --websocket obsws://{{envDefault "OBS_HOST" "localhost"}}:{{envDefault "OBS_PORT" "4455"}}/{{env "OBS_WEBSOCKET_PASSWORD"}} scene transition-duration {{.duration}}
|
|
|
|
# Toggle Studio Mode (preview + program with a Transition button).
|
|
studio_mode_toggle:
|
|
exec: |
|
|
{{envDefault "OBS_CMD" "/usr/local/bin/obs-cmd"}} --websocket obsws://{{envDefault "OBS_HOST" "localhost"}}:{{envDefault "OBS_PORT" "4455"}}/{{env "OBS_WEBSOCKET_PASSWORD"}} scene studio-mode-toggle
|
|
|
|
# In Studio Mode, set the scene shown on the preview side.
|
|
preview_scene:
|
|
params:
|
|
scene: "Scene 1"
|
|
exec: |
|
|
{{envDefault "OBS_CMD" "/usr/local/bin/obs-cmd"}} --websocket obsws://{{envDefault "OBS_HOST" "localhost"}}:{{envDefault "OBS_PORT" "4455"}}/{{env "OBS_WEBSOCKET_PASSWORD"}} scene preview-set "{{.scene}}"
|
|
|
|
# The Studio Mode "Transition" button — push preview to program using the current transition.
|
|
take:
|
|
exec: |
|
|
{{envDefault "OBS_CMD" "/usr/local/bin/obs-cmd"}} --websocket obsws://{{envDefault "OBS_HOST" "localhost"}}:{{envDefault "OBS_PORT" "4455"}}/{{env "OBS_WEBSOCKET_PASSWORD"}} scene studio-mode-transition
|
|
|
|
# Fire the current transition without changing scenes (e.g. to re-trigger a stinger).
|
|
transition_trigger:
|
|
exec: |
|
|
{{envDefault "OBS_CMD" "/usr/local/bin/obs-cmd"}} --websocket obsws://{{envDefault "OBS_HOST" "localhost"}}:{{envDefault "OBS_PORT" "4455"}}/{{env "OBS_WEBSOCKET_PASSWORD"}} scene transition-trigger
|
|
|
|
# Switch scene using a specific transition, overriding OBS's current transition for this cut.
|
|
# Chains set_transition + transition-duration + scene switch in one key press.
|
|
scene_switch_with_transition:
|
|
params:
|
|
scene: "Scene 1"
|
|
transition: "Fade"
|
|
duration: "300"
|
|
exec: |
|
|
{{envDefault "OBS_CMD" "/usr/local/bin/obs-cmd"}} --websocket obsws://{{envDefault "OBS_HOST" "localhost"}}:{{envDefault "OBS_PORT" "4455"}}/{{env "OBS_WEBSOCKET_PASSWORD"}} scene transition-set "{{.transition}}" && \
|
|
{{envDefault "OBS_CMD" "/usr/local/bin/obs-cmd"}} --websocket obsws://{{envDefault "OBS_HOST" "localhost"}}:{{envDefault "OBS_PORT" "4455"}}/{{env "OBS_WEBSOCKET_PASSWORD"}} scene transition-duration {{.duration}} && \
|
|
{{envDefault "OBS_CMD" "/usr/local/bin/obs-cmd"}} --websocket obsws://{{envDefault "OBS_HOST" "localhost"}}:{{envDefault "OBS_PORT" "4455"}}/{{env "OBS_WEBSOCKET_PASSWORD"}} scene switch "{{.scene}}"
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Camera — cameractrls (https://github.com/soyersoyer/cameractrls), Linux only.
|
|
#
|
|
# Drives any UVC webcam's V4L2 controls (zoom, pan/tilt, focus, exposure, white
|
|
# balance, brightness/contrast/...). Install: pacman -S cameractrls / pip.
|
|
#
|
|
# Device: set CAMERA_DEVICE in ~/.config/streamdeck-go/.env to the stable path, e.g.
|
|
# CAMERA_DEVICE=/dev/v4l/by-id/usb-046d_Logitech_BRIO_XXXX-video-index0
|
|
# (defaults to /dev/video0; any function also takes a per-key `device` param).
|
|
#
|
|
# Discover control names/ranges: cameractrls -d /dev/video0 -l
|
|
# Note: cameractrls exits 0 even for unknown controls, so poll blocks must use
|
|
# the `is` function (which greps `-l` output) rather than exit codes.
|
|
camera:
|
|
# Set one or more controls, comma-separated (eg. "zoom_absolute=150,contrast=140").
|
|
# Buttons work too: "preset=save_1", "preset=load_1", "color_preset=vivid".
|
|
set:
|
|
params:
|
|
device: ""
|
|
controls: "zoom_absolute=100"
|
|
exec: /usr/bin/cameractrls -d {{if .device}}{{.device}}{{else}}{{envDefault "CAMERA_DEVICE" "/dev/video0"}}{{end}} -c "{{.controls}}" 2>/dev/null
|
|
|
|
# Flip a control between two values (a <-> b). Works for 0/1 and menu controls.
|
|
toggle:
|
|
params:
|
|
device: ""
|
|
control: auto_exposure
|
|
a: aperture_priority_mode
|
|
b: manual_mode
|
|
exec: |
|
|
cur=$(/usr/bin/cameractrls -d {{if .device}}{{.device}}{{else}}{{envDefault "CAMERA_DEVICE" "/dev/video0"}}{{end}} -l 2>/dev/null | sed -n 's/^ {{.control}} = \([^[:space:]]*\).*/\1/p')
|
|
if [ "$cur" = "{{.a}}" ]; then v="{{.b}}"; else v="{{.a}}"; fi
|
|
/usr/bin/cameractrls -d {{if .device}}{{.device}}{{else}}{{envDefault "CAMERA_DEVICE" "/dev/video0"}}{{end}} -c "{{.control}}=$v" 2>/dev/null
|
|
|
|
# Step a numeric control by delta (negative to decrease), clamped to its min/max.
|
|
# `extra` is applied first on the same call — eg. "auto_exposure=manual_mode"
|
|
# so stepping exposure also takes the camera out of auto.
|
|
adjust:
|
|
params:
|
|
device: ""
|
|
control: contrast
|
|
delta: "16"
|
|
extra: ""
|
|
exec: |
|
|
line=$(/usr/bin/cameractrls -d {{if .device}}{{.device}}{{else}}{{envDefault "CAMERA_DEVICE" "/dev/video0"}}{{end}} -l 2>/dev/null | grep '^ {{.control}} = ')
|
|
cur=$(printf '%s\n' "$line" | sed -n 's/^ [a-z0-9_]* = \([-0-9]*\).*/\1/p')
|
|
min=$(printf '%s\n' "$line" | sed -n 's/.*min: \([-0-9]*\).*/\1/p')
|
|
max=$(printf '%s\n' "$line" | sed -n 's/.*max: \([-0-9]*\).*/\1/p')
|
|
[ -n "$cur" ] || exit 1
|
|
v=$((cur + {{.delta}}))
|
|
[ -n "$min" ] && [ "$v" -lt "$min" ] && v=$min
|
|
[ -n "$max" ] && [ "$v" -gt "$max" ] && v=$max
|
|
ctl="{{.control}}=$v"
|
|
[ -n "{{.extra}}" ] && ctl="{{.extra}},$ctl"
|
|
/usr/bin/cameractrls -d {{if .device}}{{.device}}{{else}}{{envDefault "CAMERA_DEVICE" "/dev/video0"}}{{end}} -c "$ctl" 2>/dev/null
|
|
|
|
# Poll helper for toggle keys: exit 0 when control == value.
|
|
is:
|
|
params:
|
|
device: ""
|
|
control: auto_exposure
|
|
value: aperture_priority_mode
|
|
exec: /usr/bin/cameractrls -d {{if .device}}{{.device}}{{else}}{{envDefault "CAMERA_DEVICE" "/dev/video0"}}{{end}} -l 2>/dev/null | grep -q '^ {{.control}} = {{.value}}[[:space:]]'
|