콘텐츠로 이동
문서
다운로드
한국어

상태와 이벤트

Sound Show는 인증 후와 상태가 변경될 때 현재 쇼 상태를 보냅니다. 이 스냅샷을 바탕으로 컨트롤러를 만들고 명령 참조를 사용해 변경을 요청하세요.

state 메시지는 로컬 상태 전체를 대체합니다. hello_ok 이후, 전반적인 변경이나 프로젝트 변경 후, 그리고 request_state의 응답으로 자동 수신합니다. 이 축약 예제는 주요 필드만 보여 줍니다. 실제 프로젝트에는 값이 채워진 배열과 추가 메타데이터가 있습니다.

{
"type": "state",
"protocol_version": 2,
"server_time_ms": 1712345678901,
"state": {
"project": { "name": "My show", "categories": [] },
"players": [],
"playable_runtime": [],
"playlists": { "current_index": 0, "playlists": [] },
"instructions": { "current_index": 0, "players": [] },
"timers": [],
"logical_devices": [],
"control": {
"next_sound_volume": 70,
"selected_category_id": null,
"low_volume_active": false,
"volume_level_presets": [],
"active_volume_level_ids": [],
"effective_volume_level_id": null
},
"capabilities": { "audio_controls": [], "manual_grid_layout": false },
"audio_controls": { "revision": 1, "logical_devices": [], "players": [] },
"lights": { "revision": 1, "lights": [] }
}
}

프로젝트 누락이나 null, 빈 배열, 선택 필드, 새로 추가된 알 수 없는 필드를 처리하세요. 이 문서는 현재 v2 구현을 설명합니다. 이전 Sound Show 2 빌드는 필드, 명령, 기능이 더 적을 수 있으므로 선택 기능을 사용하기 전에 hello_ok.features와 상태의 기능 정보를 확인하세요.

실시간 도메인 스냅샷

“실시간 도메인 스냅샷” 섹션

전체 상태 이후 대부분의 실행 업데이트는 이름이 붙은 상태 부분인 도메인 하나만 대체합니다. 빈 배열에도 의미가 있습니다. 예를 들어 players: []는 현재 플레이어를 모두 지웁니다. 배열 항목을 이전 목록과 병합하지 마세요.

메시지 type 대체할 로컬 필드 전송 조건
control_snapshot control 볼륨 레벨 명령에 대한 직접 응답. runtime_snapshots가 있으면 모든 운영자의 제어 변경.
players_snapshot players 플레이어 변경.
instructions_snapshot instructions runtime_snapshots가 있을 때.
playlists_snapshot playlists runtime_snapshots가 있을 때.
playable_runtime_snapshot playable_runtime runtime_snapshots가 있을 때.
logical_devices_snapshot logical_devices 출력 변경.
timers_snapshot timers 타이머 변경.
audio_controls_snapshot audio_controls 효과 변경. revision 비교.
lights_snapshot lights 조명 변경. revision 비교.

각 도메인 메시지에는 같은 이름의 데이터 필드와 server_time_ms가 있습니다. 예:

{
"type": "players_snapshot",
"players": [],
"server_time_ms": 1712345678901
}

오디오 제어와 조명 메시지에는 도메인 리비전도 포함됩니다.

{
"type": "audio_controls_snapshot",
"revision": 5,
"audio_controls": { "revision": 5, "logical_devices": [], "players": [] },
"server_time_ms": 1712345678901
}

이 두 도메인에서는 이미 적용한 리비전보다 낮은 메시지를 무시하세요. 서로 다른 도메인의 리비전을 비교하지 마세요. 전체 state는 기준을 초기화합니다. 연결이 끊기면 모든 로컬 상태와 리비전 추적을 지우세요. 다른 도메인은 수신 순서대로 적용합니다.

hello.features에서 runtime_snapshots를 요청하세요. 이 기능이 없으면 직접 제어 응답을 제외한 제어, 명령 시퀀스, 재생 목록 변경은 합쳐진 전체 상태로 전달됩니다. 이 기능이 없는 클라이언트에서는 재생 항목의 활동만 바뀌어도 전체 상태를 보내지는 않습니다.

실행 중 변경은 50 ms 주기로 모아서 전달합니다. 매 50 ms마다 메시지가 오거나 모든 중간 변경을 기록한 이벤트 로그를 보장하지는 않습니다. 전체 상태가 대기 중인 도메인 업데이트를 대체할 수 있습니다. 모든 현재 스냅샷 유형을 처리하는 리듀서는 JavaScript 예제를 참고하세요.

목적 사용할 값
프로젝트 버튼 시작 project.categories[].playables[].id를 playable_id로 사용.
카테고리 선택 project.categories[].id를 category_id로 사용.
실행 중인 인스턴스 하나 정지, 일시 정지, 탐색, 조정 players[].id를 player_id로 사용.
출력 제어 logical_devices[].id를 logical_device_id로 사용.
볼륨 프리셋 활성화 control.volume_level_presets[].id를 level_id로 사용.
효과 조정 설명자의 id를 control_id로 사용하고 현재 플레이어 또는 논리 장치 대상을 지정.
조명 레벨 설정 lights.lights[].id를 project_light_id로 사용.
재생 목록 항목 재생 현재 목록과 대기열의 0부터 시작하는 위치.
명령 시퀀스 항목 선택 현재 명령 플레이어의 0부터 시작하는 위치와 항목의 path.
타이머 제어 현재 0부터 시작하는 위치를 timer_index로 사용.

플레이어 ID는 실행 중인 인스턴스를 나타내므로 영구적인 프로젝트 식별자로 저장하지 마세요. 재생 항목 하나에 여러 활성 플레이어가 있을 수 있습니다. 현재 플레이어 스냅샷에서 일치하는 source_playable_id 또는 state_playable_id를 찾아 원하는 인스턴스를 명시적으로 선택하세요. 이름은 표시에 유용하지만 고유하지 않을 수 있습니다.

프로젝트와 재생 항목

“프로젝트와 재생 항목” 섹션

project.categories[]는 카테고리의 id, name, color, is_hidden, 선택적 icon_url, playables를 포함합니다. 재생 항목에는 다음이 있습니다.

{
"id": "playable-id",
"name": "Wind",
"kind": "sound",
"color": "#3b82f6",
"tags": [],
"is_searchable": true,
"runtime_state_key": "playable-id",
"target_category_id": null
}

일반적인 유형은 sound, video, image, random, timer, playlist, action, webbrowser, category_link, instruction입니다. 새 유형이나 unsupported도 처리하세요. 선택적 메타데이터에는 icon_url, 키보드 단축키, 설정된 클릭 동작 ID가 있습니다.

playable_runtime의 항목에는 state_playable_id, play_count, is_locked, is_playing가 있습니다. 재생 항목의 runtime_state_key, 해당 필드가 없으면 id를 state_playable_id와 연결하세요. 연결된 재생 항목은 활동 및 잠금 상태를 공유할 수 있습니다. 이 프로젝트 버튼 잠금은 실행 중인 플레이어의 제어 잠금과 다릅니다.

카테고리는 선택적으로 grid_layout를 포함하며 그 안에 enabled, columns, rows, pages, positions, revision가 있습니다. 사용하기 전에 capabilities.manual_grid_layout를 확인하세요. 클라이언트는 이 메타데이터를 무시하고 일반 재생 항목 목록을 표시해도 됩니다. 레이아웃 편집용 원격 명령은 없습니다.

{
"id": "runtime-player-id",
"kind": "sound",
"title": "Wind",
"color": "#3b82f6",
"panel_position": 0,
"position_seconds": 12.3,
"duration_seconds": 95,
"start_time_seconds": 5,
"stop_time_seconds": 90,
"timeline_duration_seconds": 95,
"volume_percent": 70,
"muted": false,
"is_playing": true,
"is_locked": false,
"loop_enabled": true,
"output_logical_device_id": "main",
"source_playable_id": "playable-id",
"state_playable_id": "playable-id"
}

표시에는 volume_percent를 그대로 사용하세요. 별도의 원시 volume 필드가 있더라도 100을 곱해 백분율로 만드는 값은 아닙니다. 앱은 비선형 볼륨 스케일을 사용할 수 있습니다.

타임라인 필드는 초 단위입니다. start_time_seconds, stop_time_seconds, timeline_duration_seconds는 없거나 null일 수 있습니다. 탐색 시 잘린 재생 구간을 원본 타임라인에 대응시키는 데 사용하세요. 다른 선택 필드는 remaining_playback_seconds, markers, icon_url입니다.

logical_devices[]는 id, label, display_label, color, output_role, available, selected, volume_percent를 제공합니다. 표시에는 display_label, 명령에는 id를 사용하세요.

control는 다음 시작 볼륨과 선택한 카테고리, volume_level_presets, active_volume_level_ids, effective_volume_level_id를 제공합니다. 프리셋에는 id, name, direction(both, up_only, down_only 중 하나), target_volume_percent, fade_down_ms, fade_up_ms가 있습니다. low_volume_active는 이전 컨트롤러를 지원합니다. 다른 운영자도 변경할 수 있으므로 활성 ID는 스냅샷에서 읽으세요.

재생 목록, 명령 시퀀스, 타이머

“재생 목록, 명령 시퀀스, 타이머” 섹션
도메인 주요 필드
playlists current_index와 playlists[]. 각 목록에는 id, title, autoplay, loop_enabled, queue가 있습니다. 대기열 항목에는 playable_id, title, 선택적 icon_url가 있습니다. 실행 메타데이터에는 active_item_id, active_item_title, active_player_id가 포함될 수 있습니다.
instructions current_index와 players[]. 플레이어에는 id, title, has_instruction, waiting_for_go, next_instruction_text, items가 있습니다. 항목에는 item_id, title, kind, depth, path, can_set_next, is_next_to_play, is_visibly_playing가 있습니다.
timers 숫자 id, mode, is_running, is_locked, elapsed_ms, duration_ms, remaining_ms, is_over_time를 갖는 배열. 타이머 시간은 밀리초 단위입니다.

GO 미리보기에는 path가 비어 있지 않고 kind가 group가 아닌 가장 깊은 다음 항목을 우선 사용합니다. 없으면 가장 깊은 다음 항목 중 하나를 사용하세요. 다음 항목 선택 시 제목이나 깊이로 경로를 추측하지 말고 실제 path를 사용하세요.

오디오 제어와 기능 정보

“오디오 제어와 기능 정보” 섹션

capabilities.audio_controls[]는 사용 가능한 효과 제어를 설명합니다. 각 설명자는 다음을 제공합니다.

필드 의미
id, label 프로토콜 식별자와 표시 이름.
scopes 지원 대상 유형: logical_device, player 또는 둘 다.
min, max, default_value 기본 단위의 숫자 범위와 초기화 값.
suggested_step 권장 조정 간격.
unit, scale 표시 단위와 제어 스케일. log2는 옥타브 단위의 간격을 뜻합니다.
display_multiplier 표시할 때 기본값에 곱합니다. 사용자 입력은 전송 전에 이 값으로 나눕니다.

예를 들어 0.5 비율에 display_multiplier: 100가 있으면 50%로 표시합니다. 범위를 고정하지 말고 제공된 카탈로그를 사용하세요.

audio_controls는 revision, logical_devices[], players[]를 포함합니다. 각 대상에는 kind, id, label, available, locked, 그리고 제어 ID를 기본 단위의 숫자 값에 연결하는 values 객체가 있습니다. 선형 제어는 선형으로, log2 제어는 기하급수적으로 페이드합니다.

lights는 revision와 lights[]를 포함합니다. 각 조명에는 id, name, capability(현재 level), available가 있습니다. 연결된 어댑터를 사용할 수 없으면 value_percent가 생략됩니다. 이를 0으로 처리하지 말고 사용 불가로 표시하세요.

스냅샷 항목에는 /asset/file?id=opaque-asset-id 같은 불투명한 상대 icon_url가 포함될 수 있습니다. 원격 서버의 HTTP 주소를 기준으로 해석해 HTTP로 가져오세요. 아이콘은 WebSocket 메시지가 아닙니다.

ID를 조합하거나 파일 경로를 보내지 말고 제공된 URL을 사용하세요. 현재 서버 세션 동안 아이콘을 캐시하고 재시작 후 ID가 달라질 수 있음을 고려하세요. 프로젝트 변경으로 리소스를 사용할 수 없게 될 수 있으므로 이미지 요청 실패 시 대체 표시를 제공하세요. 스냅샷은 로컬 파일 경로를 노출하지 않습니다.

인증된 연결에서는 다음을 받을 수 있습니다.

{
"type": "error",
"code": "invalid_value",
"command_id": "eq-change-1",
"message": "Human-readable error description"
}

code와 command_id는 선택 사항입니다. 오디오/조명 명령 오류에는 unknown_control, target_locked, target_not_found, invalid_value, target_unavailable, command_failed가 포함될 수 있습니다. 이전 명령 경로는 message만 반환할 수 있습니다. 사람이 읽는 문장을 고정된 오류 코드처럼 해석하지 마세요. 오류 이후에도 연결은 열려 있을 수 있으므로 스냅샷 처리를 계속하세요.

제한 클라이언트 동작
첫 메시지는 10초 이내 연결 직후 hello 전송.
동시 WebSocket 연결 16개 연결을 재사용하고 쓰지 않는 클라이언트를 닫습니다. 미인증 소켓도 포함됩니다.
수신 메시지/프레임당 64 KiB 메시지마다 작은 JSON 명령 하나를 전송합니다.
순간 최대 60개 명령, 연결당 초당 20개 보충 슬라이더 전송을 제한하고 대기 값을 합칩니다. ping과 새로고침 요청도 이 한도를 사용합니다.
같은 주소에서 60초 안에 잘못된 비밀번호 5회 비밀번호를 고치고 기다립니다. 새 업그레이드가 60초 동안 차단되며 HTTP 429와 Retry-After를 반환합니다.

속도 제한으로 거부된 명령은 적용되지 않으며 code: "rate_limited"를 반환합니다. 초과가 계속되면 연결이 닫힐 수 있습니다. 클라이언트에서 HTTP 응답을 볼 수 있으면 Retry-After를 따르세요. 인증 실패가 반복된 뒤 브라우저가 연결 실패만 보고하면 최소 60초를 기다리세요. 재생, 토글, GO를 무조건 재전송하면 동작이 두 번 실행될 수 있습니다.

hello_ok.features에 ping가 있으면 인증된 클라이언트는 {"type":"ping"}를 보내고 {"type":"pong"}를 기대할 수 있습니다. 내장 리모컨은 10초마다 ping을 보내며, 확인 시 유효한 메시지를 받은 지 25초가 넘었으면 다시 연결합니다. 변경되지 않은 도메인은 그 외에는 메시지가 없을 수 있습니다.

계속 연결하는 컨트롤러에서는:

  1. 소켓이 닫히거나 하트비트가 실패하면 즉시 명령 전송을 비활성화합니다.
  2. 캐시된 상태, 플레이어 ID, 리비전 추적을 지웁니다.
  3. 상한이 있는 대기 시간으로 재연결하고 실패가 반복되면 시간을 늘립니다. 계속 재시도하지 말고 인증 오류와 속도 제한을 따르세요.
  4. 새로운 hello를 보내고 새로운 전체 state를 기다린 뒤 대상을 다시 확인합니다.
  5. 그 상태에 따라 제어를 다시 활성화합니다. 재연결 뒤 대기 중이던 시작, 토글, GO 명령을 다시 실행하지 마세요.

연결에 속한 볼륨 누름 상태는 연결이 끊기면 해제됩니다. 서버 주소가 같더라도 재연결은 새 세션으로 취급하세요.

Sound Show의 외부 출처 허용을 변경하면 즉시 적용되고 현재 클라이언트가 끊어집니다. 비활성화 후에는 다른 출처의 브라우저가 다시 연결할 수 없습니다. 설정과 브라우저 제한은 사용자 지정 웹 리모컨을 참고하세요.