Streamingle_URP/Streamdeck/STREAMDOCK_SDK_REFERENCE.md
2025-10-28 00:02:17 +09:00

19 KiB (Stored with Git LFS)

StreamDock Plugin SDK 공식 문서 백업

출처: https://sdk.key123.vip/ GitHub: https://github.com/MiraboxSpace/StreamDock-Plugin-SDK 백업일: 2025-01-27


📋 목차

  1. SDK 개요
  2. ⚠️ 중요: 자주 발생하는 오류 및 해결법
  3. 지원 언어 및 SDK
  4. 플러그인이 수신하는 이벤트
  5. 플러그인이 전송하는 이벤트
  6. 메시지 구조
  7. 버전 요구사항

SDK 개요

Stream Dock란?

Stream Dock는 여러 개의 전용 프로그래밍 가능한 버튼을 제공하는 USB 주변기기입니다.

하드웨어 모델:

  • 293
  • 293s
  • 293N3
  • 293N4

에코시스템 구성

  1. 하드웨어 장치: 물리적인 StreamDock 버튼 장치
  2. 데스크톱 소프트웨어: 구성 및 관리 소프트웨어
  3. 플러그인 시스템: 확장 가능한 플러그인 아키텍처

SDK 설계 원칙

  1. 강력함 (Powerful): 키 이벤트 트리거를 통해 코드 실행
  2. 단순함 (Simple): JSON 기반 통신 프로토콜 사용
  3. 언어 독립적 (Language-Agnostic): WebSocket을 지원하는 모든 언어 사용 가능 (JavaScript 권장)
  4. 안전함 (Secure): 각 플러그인을 별도 프로세스로 격리
  5. 크로스 플랫폼 (Cross-Platform): macOS와 Windows 호환

주요 기능

  • 프로그래밍 가능한 버튼에 사용자 정의 액션 할당
  • 아이콘 및 레이블로 버튼 외형 커스터마이징
  • Property Inspector 인터페이스를 통한 액션 구성
  • 서드파티 플러그인 및 마켓플레이스 통합

기술 기반

WebSocket 통신을 사용하여 플러그인-호스트 간 상호작용을 수행합니다.


⚠️ 중요: 자주 발생하는 오류 및 해결법

🐛 문제: keyUp 이벤트에서 action UUID를 전달하지 않아 잘못된 액션이 실행됨

증상:

  • 올바른 버튼을 클릭했는데 다른 액션이 실행됨
  • 설정(Settings)이 비어있어서 기본값으로 폴백됨
  • didReceiveSettings에서 받은 설정에 actionType 같은 커스텀 필드가 없음

원인:

case 'keyUp':
    handleButtonClick(jsonObj.context);  // ❌ action UUID를 전달하지 않음!
    break;

StreamDock SDK는 버튼의 설정을 영구 저장하지만, 설정이 비어있거나 초기화되지 않은 경우:

  1. willAppear 이벤트에서 설정을 초기화해도 setSettings을 호출하지 않으면 저장되지 않음
  2. keyUp 이벤트에서 getCurrentSettings(context)로 가져오면 빈 객체 반환
  3. 기본값으로 폴백되어 의도하지 않은 액션 실행

해결 방법:

방법 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;

핵심 교훈:

  1. keyUp 이벤트에는 action UUID가 포함되어 있습니다 - 이를 활용하세요!
  2. Settings는 자동으로 저장되지 않습니다 - setSettings 이벤트를 명시적으로 보내야 합니다
  3. 여러 액션 타입을 처리할 때는 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: 장치 ID
  • payload:
    • 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: 장치 ID
  • payload:
    • 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: 장치 ID
  • deviceInfo: 장치 타입 및 크기 정보
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: 컨텍스트 ID
  • payload.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: 컨텍스트 ID
  • payload.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: 컨텍스트 ID
  • payload.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 개발자

참고 자료

공식 문서

GitHub

커뮤니티

라이선스

  • MIT License - 오픈소스

문서 버전: 1.0 최종 업데이트: 2025-01-27 백업 목적: AI 환각 방지 및 오프라인 참조