19 KiB (Stored with Git LFS)
StreamDock Plugin SDK 공식 문서 백업
출처: https://sdk.key123.vip/ GitHub: https://github.com/MiraboxSpace/StreamDock-Plugin-SDK 백업일: 2025-01-27
📋 목차
SDK 개요
Stream Dock란?
Stream Dock는 여러 개의 전용 프로그래밍 가능한 버튼을 제공하는 USB 주변기기입니다.
하드웨어 모델:
- 293
- 293s
- 293N3
- 293N4
에코시스템 구성
- 하드웨어 장치: 물리적인 StreamDock 버튼 장치
- 데스크톱 소프트웨어: 구성 및 관리 소프트웨어
- 플러그인 시스템: 확장 가능한 플러그인 아키텍처
SDK 설계 원칙
- 강력함 (Powerful): 키 이벤트 트리거를 통해 코드 실행
- 단순함 (Simple): JSON 기반 통신 프로토콜 사용
- 언어 독립적 (Language-Agnostic): WebSocket을 지원하는 모든 언어 사용 가능 (JavaScript 권장)
- 안전함 (Secure): 각 플러그인을 별도 프로세스로 격리
- 크로스 플랫폼 (Cross-Platform): macOS와 Windows 호환
주요 기능
- 프로그래밍 가능한 버튼에 사용자 정의 액션 할당
- 아이콘 및 레이블로 버튼 외형 커스터마이징
- Property Inspector 인터페이스를 통한 액션 구성
- 서드파티 플러그인 및 마켓플레이스 통합
기술 기반
WebSocket 통신을 사용하여 플러그인-호스트 간 상호작용을 수행합니다.
⚠️ 중요: 자주 발생하는 오류 및 해결법
🐛 문제: keyUp 이벤트에서 action UUID를 전달하지 않아 잘못된 액션이 실행됨
증상:
- 올바른 버튼을 클릭했는데 다른 액션이 실행됨
- 설정(Settings)이 비어있어서 기본값으로 폴백됨
didReceiveSettings에서 받은 설정에actionType같은 커스텀 필드가 없음
원인:
case 'keyUp':
handleButtonClick(jsonObj.context); // ❌ action UUID를 전달하지 않음!
break;
StreamDock SDK는 버튼의 설정을 영구 저장하지만, 설정이 비어있거나 초기화되지 않은 경우:
willAppear이벤트에서 설정을 초기화해도setSettings을 호출하지 않으면 저장되지 않음keyUp이벤트에서getCurrentSettings(context)로 가져오면 빈 객체 반환- 기본값으로 폴백되어 의도하지 않은 액션 실행
해결 방법:
방법 1: keyUp에서 action UUID 전달 (권장)
case 'keyUp':
console.log('🔘 버튼 클릭됨!');
console.log('🔍 Action UUID:', jsonObj.action);
handleButtonClick(jsonObj.context, jsonObj.action); // ✅ action UUID 전달
break;
// 핸들러에서 action UUID로 actionType 결정
function handleButtonClick(context, actionUUID) {
const settings = getCurrentSettings(context);
let actionType = settings.actionType;
// actionType이 없으면 action UUID로 판단
if (!actionType && actionUUID) {
if (actionUUID === 'com.example.myaction') {
actionType = 'myaction';
} else if (actionUUID === 'com.example.otheraction') {
actionType = 'otheraction';
} else {
actionType = 'default';
}
// 설정에 저장
settings.actionType = actionType;
setCurrentSettings(context, settings);
}
// actionType에 따라 처리
switch (actionType) {
case 'myaction':
handleMyAction();
break;
// ...
}
}
방법 2: willAppear에서 setSettings 호출
case 'willAppear':
let settings = jsonObj.payload?.settings || {};
// action UUID로 actionType 설정
if (jsonObj.action === 'com.example.myaction') {
settings.actionType = 'myaction';
}
buttonContexts.set(jsonObj.context, settings);
// ✅ StreamDock SDK에 영구 저장
if (websocket) {
const setSettingsMessage = {
event: 'setSettings',
context: jsonObj.context,
payload: settings
};
websocket.send(JSON.stringify(setSettingsMessage));
}
break;
방법 3: didReceiveSettings에서 보정
case 'didReceiveSettings':
if (jsonObj.payload && jsonObj.context) {
const newSettings = jsonObj.payload.settings || {};
// actionType이 없으면 action UUID로 보정
if (!newSettings.actionType && jsonObj.action) {
if (jsonObj.action === 'com.example.myaction') {
newSettings.actionType = 'myaction';
// StreamDock SDK에 저장
if (websocket) {
const setSettingsMessage = {
event: 'setSettings',
context: jsonObj.context,
payload: newSettings
};
websocket.send(JSON.stringify(setSettingsMessage));
}
}
}
buttonContexts.set(jsonObj.context, newSettings);
}
break;
핵심 교훈:
- keyUp 이벤트에는 action UUID가 포함되어 있습니다 - 이를 활용하세요!
- Settings는 자동으로 저장되지 않습니다 -
setSettings이벤트를 명시적으로 보내야 합니다 - 여러 액션 타입을 처리할 때는 action UUID를 직접 확인하는 것이 가장 안전합니다
실제 발생 사례:
- OptiTrack Marker Toggle 버튼을 클릭했는데 Camera Switch 메시지가 전송됨
- Settings가
{}로 비어있어서 기본값인camera로 폴백 - action UUID (
com.mirabox.streamingle.optitrack_marker_toggle)를 확인하지 않아서 발생
디버깅 팁:
case 'keyUp':
console.log('============================================');
console.log('버튼 클릭!');
console.log('Context:', jsonObj.context);
console.log('Action UUID:', jsonObj.action); // ⭐ 반드시 로그로 확인
console.log('Settings:', getCurrentSettings(jsonObj.context));
console.log('============================================');
handleButtonClick(jsonObj.context, jsonObj.action);
break;
지원 언어 및 SDK
StreamDock Plugin SDK는 다양한 프로그래밍 언어를 지원합니다:
1. JavaScript SDK (SDJavaScriptSDK)
- 순수 JavaScript 구현
- 브라우저 기반 플러그인
- 가장 단순한 구조
2. Node.js SDK V2 (SDNodeJsSDKV2) ⭐ 권장
- Node.js 20.8.1 내장 (StreamDock 3.10.188.226+)
- log4js 로깅 시스템
- 파일 기반 로깅 지원
- WebSocket 재연결 로직
3. Node.js SDK (SDNodeJsSDK) - Legacy
- 구 버전 Node.js SDK
- V2 사용 권장
4. Vue.js SDK (SDVueSDK)
- Vue.js 프레임워크 기반
- 반응형 UI 컴포넌트
- TypeScript 지원
5. C++ SDK (StreamDockCPPSDK)
- 네이티브 C++ 구현
- 고성능 플러그인
- Visual Studio 2022 지원
6. Qt SDK (SDQtSDK)
- Qt 프레임워크 기반
- 크로스 플랫폼 UI
- C++ 기반
7. Python SDK (SDPythonSDK)
- 순수 Python 구현
- PyInstaller로 exe 패키징
- 타입 힌팅 지원
플러그인이 수신하는 이벤트
공통 이벤트 (Plugin & Property Inspector)
didReceiveSettings
설정이 변경될 때 트리거됩니다.
파라미터:
action: 액션 식별자context: 컨텍스트 값device: 장치 정보payload: 설정 데이터 및 좌표 포함
예시:
{
"event": "didReceiveSettings",
"action": "com.example.plugin.action",
"context": "unique_context_id",
"device": "device_id",
"payload": {
"settings": {
"key": "value"
},
"coordinates": {
"column": 0,
"row": 0
}
}
}
didReceiveGlobalSettings
전역 설정이 변경될 때 트리거됩니다.
파라미터:
payload: 전역 설정 데이터
플러그인 전용 이벤트
입력 이벤트
keyDown
사용자가 키를 누를 때 발생합니다.
파라미터:
action: 액션 식별자context: 컨텍스트 값device: 장치 IDpayload:settings: 액션 설정coordinates: 키 좌표state: 현재 상태 (0 또는 1)isInMultiAction: Multi-Action 여부
예시:
{
"event": "keyDown",
"action": "com.example.plugin.action",
"context": "context_id",
"device": "device_id",
"payload": {
"settings": {},
"coordinates": {"column": 2, "row": 1},
"state": 0,
"isInMultiAction": false
}
}
keyUp
사용자가 키를 뗄 때 발생합니다. (파라미터는 keyDown과 동일)
dialDown
다이얼(엔코더)을 누를 때 발생합니다.
dialUp
다이얼(엔코더)을 뗄 때 발생합니다.
dialRotate
다이얼(엔코더)을 회전할 때 발생합니다.
파라미터:
payload.ticks: 회전 틱 수 (양수=시계방향, 음수=반시계방향)payload.pressed: 다이얼이 눌려진 상태인지 여부
예시:
{
"event": "dialRotate",
"action": "com.example.plugin.action",
"context": "context_id",
"device": "device_id",
"payload": {
"ticks": 3,
"pressed": false,
"settings": {}
}
}
라이프사이클 이벤트
willAppear
액션 인스턴스가 화면에 나타날 때 발생합니다.
발생 시점:
- 시작 시
- 프로필 전환 시
- 키 할당 시
파라미터:
action: 액션 식별자context: 컨텍스트 값device: 장치 IDpayload:settings: 저장된 설정coordinates: 버튼 위치state: 현재 상태isInMultiAction: Multi-Action 여부
예시:
{
"event": "willAppear",
"action": "com.mirabox.streamingle.optitrack_marker_toggle",
"context": "button_context_123",
"device": "streamdock_device_1",
"payload": {
"settings": {
"currentState": 0
},
"coordinates": {"column": 0, "row": 0},
"state": 0,
"isInMultiAction": false
}
}
willDisappear
프로필 전환 또는 액션 삭제 시 트리거됩니다.
titleParametersDidChange
사용자가 제목 또는 제목 파라미터를 수정할 때 발생합니다.
포함 정보:
- 폰트 설정
- 정렬 방식
- 제목 텍스트
시스템 이벤트
deviceDidConnect
장치가 연결될 때 발생합니다.
파라미터:
device: 장치 IDdeviceInfo: 장치 타입 및 크기 정보
deviceDidDisconnect
장치 연결이 해제될 때 발생합니다.
applicationDidLaunch
지정된 애플리케이션이 실행될 때 발생합니다.
식별자:
- macOS: Bundle ID
- Windows: .exe 파일명
applicationDidTerminate
지정된 애플리케이션이 종료될 때 발생합니다.
systemDidWakeUp
컴퓨터가 절전 모드에서 깨어날 때 발생합니다.
주의: 여러 번 발생할 수 있음
Property Inspector 이벤트
propertyInspectorDidAppear
Property Inspector가 표시될 때 발생합니다.
propertyInspectorDidDisappear
Property Inspector가 숨겨질 때 발생합니다.
sendToPlugin
Property Inspector에서 플러그인으로 데이터를 전송합니다.
예시 (Property Inspector → Plugin):
// Property Inspector
sendToPlugin('update_settings', { value: 123 });
// Plugin
websocket.onmessage = (event) => {
const jsonObj = JSON.parse(event.data);
if (jsonObj.event === 'sendToPlugin') {
const payload = jsonObj.payload;
console.log('Received:', payload);
}
};
플러그인이 전송하는 이벤트
공통 이벤트 (Plugin & Property Inspector)
setSettings
액션 인스턴스의 데이터를 영구적으로 저장합니다.
구조:
{
"event": "setSettings",
"context": "context_id",
"payload": {
"key1": "value1",
"key2": 123
}
}
사용 예:
function saveSettings(context, settings) {
const message = {
event: 'setSettings',
context: context,
payload: settings
};
websocket.send(JSON.stringify(message));
}
getSettings
저장된 설정을 요청합니다. didReceiveSettings 이벤트로 응답을 받습니다.
구조:
{
"event": "getSettings",
"context": "context_id"
}
setGlobalSettings
플러그인 전역 설정을 저장합니다. 모든 액션에서 접근 가능합니다.
구조:
{
"event": "setGlobalSettings",
"context": "plugin_uuid",
"payload": {
"globalKey": "globalValue"
}
}
getGlobalSettings
전역 설정을 요청합니다. didReceiveGlobalSettings로 응답을 받습니다.
openUrl
기본 브라우저에서 URL을 엽니다.
구조:
{
"event": "openUrl",
"payload": {
"url": "https://example.com"
}
}
logMessage
디버그 메시지를 로그 창에 작성합니다.
구조:
{
"event": "logMessage",
"payload": {
"message": "Debug message here"
}
}
플러그인 전용 이벤트
setTitle
액션 인스턴스의 제목을 동적으로 업데이트합니다.
파라미터:
context: 컨텍스트 IDpayload.title: 제목 텍스트 (줄바꿈:\n)payload.target: 대상 (0=하드웨어+소프트웨어, 1=하드웨어만, 2=소프트웨어만)payload.state: 상태 (다중 상태 액션용)
예시:
function setButtonTitle(context, title) {
const message = {
event: 'setTitle',
context: context,
payload: {
title: title,
target: 0
}
};
websocket.send(JSON.stringify(message));
}
// 사용
setButtonTitle(context, '마커\nON');
setImage
액션의 이미지를 변경합니다.
지원 형식:
- Base64 인코딩 이미지
- SVG 형식
파라미터:
context: 컨텍스트 IDpayload.image: Base64 또는 SVG 문자열payload.target: 대상payload.state: 상태
예시:
function setButtonImage(context, imageBase64) {
const message = {
event: 'setImage',
context: context,
payload: {
image: imageBase64,
target: 0
}
};
websocket.send(JSON.stringify(message));
}
showAlert
액션에 임시 경고 아이콘을 표시합니다.
구조:
{
"event": "showAlert",
"context": "context_id"
}
showOk
액션에 임시 체크마크 확인 아이콘을 표시합니다.
구조:
{
"event": "showOk",
"context": "context_id"
}
setState
다중 상태 액션의 상태를 변경합니다.
파라미터:
context: 컨텍스트 IDpayload.state: 상태 번호 (0, 1, 2, ...)
예시:
function setButtonState(context, state) {
const message = {
event: 'setState',
context: context,
payload: {
state: state
}
};
websocket.send(JSON.stringify(message));
}
// 사용: ON(0) → OFF(1) 토글
const newState = currentState === 0 ? 1 : 0;
setButtonState(context, newState);
sendToPropertyInspector
플러그인에서 Property Inspector로 데이터를 전송합니다.
예시 (Plugin → Property Inspector):
// Plugin
function sendToPI(context, action, payload) {
const message = {
event: 'sendToPropertyInspector',
action: action,
context: context,
payload: payload
};
websocket.send(JSON.stringify(message));
}
// Property Inspector
window.connectElgatoStreamDeckSocket = (inPort, inUUID, inEvent, inInfo) => {
websocket.onmessage = (evt) => {
const jsonObj = JSON.parse(evt.data);
if (jsonObj.event === 'sendToPropertyInspector') {
console.log('Received from plugin:', jsonObj.payload);
}
};
};
메시지 구조
표준 메시지 형식
모든 WebSocket 메시지는 다음 표준 JSON 구조를 따릅니다:
{
"event": "이벤트_이름",
"action": "com.developer.plugin.action_id",
"context": "unique_context_id",
"device": "device_id",
"payload": {
// 이벤트별 데이터
}
}
필드 설명
- event: 이벤트 타입 (필수)
- action: 액션 UUID (일부 이벤트에서 선택)
- context: 고유 컨텍스트 식별자 (필수)
- device: 장치 ID (장치 관련 이벤트)
- payload: 이벤트별 데이터 객체
버전 요구사항
StreamDock 소프트웨어 버전
Windows
- 최소 버전:
3.10.188.226이상 - Node.js 내장: 버전 20.8.1
- manifest.json 설정:
{ "Nodejs": { "Version": "20" } }
macOS
- 최소 버전:
3.10.189.0313(Node.js 내장 예정) - 현재는 외부 Node.js 설치 필요
SDK 버전 호환성
| SDK | 최소 SW 버전 | Node.js 버전 | 권장 사용 |
|---|---|---|---|
| SDNodeJsSDKV2 | 3.10.188.226 | 20.8.1 | ✅ 권장 |
| SDNodeJsSDK | 모든 버전 | 외부 설치 | ⚠️ Legacy |
| SDJavaScriptSDK | 모든 버전 | N/A | ✅ 간단한 플러그인 |
| SDVueSDK | 3.10.188.226 | 20.8.1 | ✅ 복잡한 UI |
| StreamDockCPPSDK | 모든 버전 | N/A | ✅ 고성능 |
| SDPythonSDK | 모든 버전 | N/A | ✅ Python 개발자 |
참고 자료
공식 문서
- 메인: https://sdk.key123.vip/
- Getting Started: https://sdk.key123.vip/en/guide/overview.html
- 수신 이벤트: https://sdk.key123.vip/en/guide/events-received.html
- 전송 이벤트: https://sdk.key123.vip/en/guide/events-sent.html
- Manifest 문서: https://sdk.key123.vip/en/guide/manifest.html
GitHub
- SDK 리포지토리: https://github.com/MiraboxSpace/StreamDock-Plugin-SDK
- 플러그인 예제: https://github.com/MiraboxSpace/StreamDock-Plugins
커뮤니티
- Email: service@key123.vip
- Discord: 공식 Discord 서버
- 플랫폼: https://space.key123.vip/ (플러그인 마켓플레이스)
라이선스
- MIT License - 오픈소스
문서 버전: 1.0 최종 업데이트: 2025-01-27 백업 목적: AI 환각 방지 및 오프라인 참조