Ir para o conteúdo

API

O servidor do DroidDeck expõe uma API REST e um hub SignalR na porta 4787. O app do celular e o configurador usam os dois, e você pode usá-los para criar seus próprios clientes ou automações.

Autenticação

Todo endpoint exige a chave de API, exceto GET /api/ping e GET /api/pairing/local-key. Envie a chave de uma destas formas:

  • header X-API-KEY: <key>
  • header Authorization: Bearer <key>
  • query string ?access_token=<key> (usada no WebSocket do SignalR)

GET /api/pairing/local-key só retorna {key} para requisições do próprio PC (loopback) e recusa requisições cross-site do navegador. O CORS só permite origens em loopback e em faixas de rede privada.

Falhas de integração retornam 502 com {error}.

Ações privilegiadas

Atalho de teclado, abrir programa, ativar janela e script executam código no PC, e a requisição HTTP sai com o acesso de rede do PC, então ficam protegidas até de quem tem a chave. Requisições que não vêm do próprio PC (loopback) não podem:

  • executá-las pelo execute; use o press com um botão salvo no PC;
  • criar um botão com uma dessas ações, ou mudar os parâmetros dela, pelo POST profiles. Mover, renomear, mudar a cor e apagar esses botões continua liberado.

Uma multi-ação conta como privilegiada se qualquer passo dela for. Requisições recusadas retornam 403 com {error}.

Conexões de um celular pelo cabo USB chegam pelo loopback, através do adb; o servidor as reconhece e as trata como remotas, não como o PC.

Exemplos

# Is the server up?
curl http://192.168.0.10:4787/api/ping

# Toggle Discord mute
curl -X POST http://192.168.0.10:4787/api/StreamDeck/execute \
  -H "X-API-KEY: $KEY" -H "Content-Type: application/json" \
  -d '{"type":"discord","parameters":{"operation":"toggleMute"}}'

# Mute Spotify
curl -X POST http://192.168.0.10:4787/api/StreamDeck/execute \
  -H "X-API-KEY: $KEY" -H "Content-Type: application/json" \
  -d '{"type":"mixer","parameters":{"operation":"mute","processName":"Spotify"}}'

Deck: /api/StreamDeck

Método Rota Descrição
GET profiles Lista os perfis.
GET profiles/{id} Retorna um perfil.
POST profiles Cria ou atualiza um perfil. Dispara ReceiveDeckUpdate.
DELETE profiles/{id} Apaga um perfil. Dispara ReceiveDeckUpdate.
POST press Aperta um botão salvo ({profileId, buttonId, gesture}; gesture é tap (padrão), longPress ou doubleTap): o servidor executa a ação gravada no perfil para esse gesto. É o que o celular usa.
POST execute Executa uma ação avulsa ({type, parameters}). De fora do PC, ações privilegiadas retornam 403.
GET / POST layout Lê / define o tamanho da grade ({rows, columns}).

Payload da ação

{ "type": "hotkey", "parameters": { "keys": "^+m" } }

Todos os valores de parâmetros são strings. A tabela abaixo é um resumo; a fonte oficial, legível por máquina, é o GET /api/actions/schema, que lista cada tipo de ação com os parâmetros (fields[].key), os valores aceitos (options) e quando cada campo vale (visibleWhen).

type Parâmetros
hotkey keys
launch_app path, arguments?
activatewindow windowName (título exato da janela)
media command: playpause·play·pause·next·previous·stop; sessionId?
mixer operation: toggleMute·mute·unmute·setVolume·volumeUp·volumeDown; processName ou deviceId (default ou o id de um endpoint); deviceKind: output·input; volume (0–100); step (1–50)
multi steps: string com um array JSON [{"type","parameters","delayMs","if"}]; if é opcional: {check, value, negate}, com check entre obs_streaming·obs_recording·discord_mute·discord_deaf·toggle_on (value = id do botão)·app_running·app_focused (value = processo)·time_between (value = 08:00-18:00)·weekday (value = 1-5, 0 = domingo)
discord operation: toggleMute·mute·unmute·toggleDeafen·joinChannel (channelId)·disconnect·inputVolumeUp/Down·outputVolumeUp/Down (delta)·setInputVolume·setOutputVolume (value)·toggleVoiceMode·setVoiceMode (mode: VOICE_ACTIVITY·PUSH_TO_TALK)·userMute (userId)·userVolume (userId, value 0–200)
obs operation: setScene (scene)·toggleRecord·startRecord·stopRecord·toggleStream·toggleVirtualCam·toggleReplayBuffer·saveReplay·toggleInputMute (inputName)
soundboard operation: play·stop; source: myinstants (id, url, title) ou discord (soundId, guildId)
tuya deviceId, code, operation: toggle·set; value, valueType
homeassistant operation: toggle·turn_on·turn_off·brightness (step)·scene·script·display·service (service = domínio.serviço, data em JSON); entityId
spotify operation: playPause·next·previous·like·shuffle·repeat·volume (step)·playlist (uri, shuffleOn)·nowPlaying
twitch operation: marker (description)·clip·ad (length)·chat (message)·poll (question, choices uma por linha, duration)·title (title)·status
youtube operation: chat (message)·status
streamerbot action (id ou nome), args (chave=valor por linha, ou JSON)
vtubestudio operation: hotkey (hotkey: id ou nome)·model (model: id do modelo)
voicemeeter operation: toggle_mute·mute·unmute·set_gain (gain dB)·adjust_gain (step dB) com target: strip·bus e index (1–8)·macro (button)·param_set (parameter, value)·param_toggle (parameter)
hue operation: toggle·on·off·brightness (brightness %)·brightness_step (step %) com target: light·group e id·scene (scene)
keylight host (IP ou IP:porta); operation: toggle·on·off·brightness (brightness)·brightness_step (step)·temperature (kelvin)·temperature_step (kelvinStep)
govee ip; operation: toggle·on·off·brightness (brightness)·color (color #RRGGBB)·temperature (kelvin)
mqtt topic, payload, qos (0–2), retain (true/false)
script source: inline (language: powershell·python·cmd; code) ou file (path); arguments
http method: POST·GET·PUT·PATCH·DELETE; url; headers (um Nome: valor por linha); em POST, PUT e PATCH, contentType (application/json·application/x-www-form-urlencoded·text/plain) e body
timer mode: countdown (durationMin)·stopwatch·pomodoro (workMin, breakMin)·counter (step); operation: toggle·reset·skip·increment·decrement; target: self ou o id de outro botão de timer
windows operation: setDefaultAudio (deviceKind: output·input; deviceId; altDeviceId para alternar entre dois, ou none)·lock·sleep·displayOff
steam appId: o id numérico do app na Steam (de GET /api/steam/games)

open_profile e back existem só no celular.

Áudio: /api/v1/Mixer, /api/v1/Media

Método Rota Descrição
GET Mixer/out, Mixer/in Dispositivos de saída / entrada com suas sessões de app.
GET Mixer/out/{id}, Mixer/in/{id} Um dispositivo.
PUT Mixer/out/{id}, Mixer/in/{id} {session, volume?, mute?}: session é o id de um processo, ou -1 para o dispositivo inteiro.
GET Media/sessions Sessões de mídia, com miniaturas.
GET Media/sessions/{id} Uma sessão.
POST Media/sessions/{id}/command {command}.

Um perfil é {id, name, rows, columns, isDefault, buttons, autoSwitchApps}; um botão tem action, longPressAction e doubleTapAction (cada um {type, parameters} ou null). A regra das ações privilegiadas vale para os três.

Botões de dois estados acrescentam toggle (bool), activeAction (roda no toque que desliga), activeLabel, activeIconName e activeIconBase64. Botões de informação usam dynamicType (cpu_monitor, memory_monitor, gpu_monitor, network_monitor, clock, weather, now_playing, command, url, notifications, volume_slider, ai_usage) com dynamicParameters (city, units, command, url, path, format, interval, app, target, provider, show…); command e url seguem a regra das ações privilegiadas. Botões grandes acrescentam columnSpan e rowSpan (padrão 1; row/column é a casa do canto superior esquerdo).

Método Caminho Descrição
GET /api/slider?target=… Controle deslizante: {volume, mute, label, max, text} atual. target: output, input, app (app), obs (inputName), discord_user (userId), monitor, media, tuya (deviceId), homeassistant (entityId), hue (hueTarget, id), govee (ip), keylight (host). max é 100, exceto Discord (200) e música (duração em segundos). 404 quando o alvo não existe no momento; 503 {error} quando a integração não responde.
PUT /api/slider {target, app, params, volume} (0 até max). Devolve o estado novo.
GET /api/slider/options?target=tuya\|discord_user Lista {value, label} de lâmpadas Smart Life com brilho ou de pessoas na call.

Biblioteca: backup, Steam, apps

Método Caminho Descrição
GET /api/backup Baixa todos os perfis num .droiddeck (zip).
POST /api/backup Restaura um .droiddeck (corpo = o arquivo). Perfis com o mesmo id são substituídos. De fora do PC, recusa com 403 se for criar botões privilegiados.
GET /api/steam/games Jogos instalados na Steam ({value: appId, label: nome}).
GET /api/steam/icon/{appId} {iconBase64}: PNG quadrado recortado da capa do jogo.
GET /api/system/apps Programas com janela visível (para a troca automática de perfil).
GET /api/system/info {name, version}: o nome do PC no Windows, usado para identificar os PCs pareados.
GET /api/info Valores atuais dos botões de informação, o que está tocando e os botões de dois estados ligados.
GET / POST /api/macro, /api/macro/start, /api/macro/stop Gravação de macro pelo teclado. Só do próprio PC (senão 403); stop devolve {steps} no formato da multiação.
POST /api/import/streamdeck Importa um .streamDeckProfile (corpo = o arquivo, até 50 MB) como perfis novos. Devolve {profiles, buttons, unsupported, stripped, firstProfileId}; de fora do PC, botões privilegiados entram sem a ação e stripped conta quantos. 400 com {error} se não for um perfil do Stream Deck.
GET /api/share/{profileId} {data, url, qr, images}: o perfil comprimido num link (url é a página de compartilhamento do site com #data); qr diz se cabe num QR code; images, se imagens próprias ficaram de fora.
GET /api/share/{profileId}/qr O link de compartilhamento como QR code em PNG.
POST /api/share/import {data} de um link compartilhado → salvo como perfil novo. Devolve {profileId, name, buttons, stripped}; mesma regra das ações privilegiadas da importação do Stream Deck.

Automações: /api/triggers

Método Caminho Descrição
GET /api/triggers A lista de automações.
PUT /api/triggers Troca a lista inteira. Devolve a lista salva.
POST /api/triggers/{id}/run Aperta o botão da automação agora. 404 se a automação ou o botão não existir.

Uma automação é {id, name, enabled, event, time, days, app, profileId, buttonId, if}. event é schedule (time HH:mm, days como 1-5), app_start / app_exit (app), resume ou startup; if é uma condição opcional no formato da multiação. Como as automações só apertam botões salvos, elas seguem as regras com que esses botões foram criados.

Tipos de ação: /api/actions

Método Caminho Descrição
GET schema Todos os tipos de ação, com título, ícone, categoria e campos. O editor monta o formulário a partir disto.
GET state/{type} Estado ao vivo atual de um tipo (o mesmo conteúdo do evento SignalR dele).
GET timer/targets Botões de timer que outro botão pode controlar.
GET windows/audio-devices?kind=output\|input Dispositivos de áudio ativos ({value, label}).

O schema é a fonte oficial dos parâmetros das ações: o editor do app monta os formulários a partir dele, então um tipo de ação ou campo novo aparece aqui primeiro. Os textos seguem o Accept-Language. Uma resposta resumida:

[
  {
    "type": "steam",
    "title": "Jogo da Steam",
    "icon": "sports_esports",
    "category": "Sistema",
    "privileged": false,
    "stateful": false,
    "editor": "steam",
    "stateEvent": null,
    "fields": [
      {
        "key": "appId",
        "label": "Jogo",
        "kind": "select",
        "required": true,
        "optionsSource": "/api/steam/games",
        "visibleWhen": null
      }
    ]
  }
]

Integrações

Prefixo Endpoints
/api/discord POST config {clientId, clientSecret} · POST connect · GET state · GET guilds · GET channels/{guildId} · GET voice-channel · POST mute · POST deafen · GET soundboard-sounds · POST play-soundboard
/api/obs POST config {host, port, password} · POST connect · GET state · GET scenes · GET audio-inputs · GET sources · GET scene-items?scene= · GET filters?source=
/api/Soundboard GET search?q= · GET trending · POST play · POST stop · GET state · GET devices · GET/POST config
/api/tuya POST pair/start {userCode} · POST pair/poll (410 quando o QR expirou) · GET state · POST connect · POST devices/refresh · POST command {deviceId, code, value}
/api/govee GET devices: procura na rede os aparelhos Govee com Controle LAN (cerca de 2 s); lista de {value: ip, label}
/api/integrations/{nome} homeassistant, spotify, twitch, youtube, hue, mqtt, streamerbot, vtubestudio. GET estado {connected, configured, account, message, loginPending, userCode, verificationUri, redirectUri, fields} · POST os campos da configuração (conecta ou abre o login no navegador do PC) · DELETE esquece a conta · GET options/{lista} (entities?operation=, playlists, Hue items?target=·scenes, Streamer.bot actions, VTube Studio hotkeys·models) · GET callback (retorno do OAuth, sem chave da API)

As respostas seguem o Accept-Language do pedido: com en, tipos de ação, rótulos dos campos e mensagens de erro vêm em inglês.

Hub SignalR: /deckHub

Conecte com ?access_token=<key>. O hub só envia eventos; os comandos passam pelo REST.

Evento Payload
ReceiveSystemStats {cpuUsage, ramUsage, ramTotal, ramAvailable, netUpKBps, netDownKBps, gpuUsage}: a cada segundo
ReceiveMediaStatus {playing}
ReceiveMediaState {sessionId, command, playing}
ReceiveVolumeChange {deviceId, type, data}
ReceiveMuteState {processName, muted}
ReceiveDeckUpdate {profileId}
ReceiveLayoutUpdate {rows, columns}
ReceiveDiscordState Conexão, mudo/ensurdecido, canal, volumes, modo de voz, participantes
ReceiveObsState Conexão, cena atual, gravação/transmissão/câmera virtual/replay, cenas, entradas de áudio
ReceiveTuyaState Conexão, push, dispositivos com status e funções
ReceiveTuyaDeviceState {deviceId, status}: só os valores alterados
ReceiveHueState, ReceiveMqttState, ReceiveStreamerBotState, ReceiveVTubeStudioState {connected} (o VTube Studio manda também model)
ReceiveTimerState {buttonId: {mode, running, finished, elapsedMs, durationMs, count, phase, cycles}}: a cada mudança
ReceiveWindowsState {defaultOutput, defaultInput}: ids dos dispositivos de áudio padrão
ReceiveAutoProfile {profileId, app}: perfil do app em foco; profileId nulo quando o app não tem perfil
ReceivePlaybackState {playing, title}: soundboard
ReceiveButtonToggles {buttonId: true}: botões de dois estados ligados
ReceiveInfoState {buttonId: {text, sub, code, isDay, error}}: botões de clima, comando e URL
ReceiveNowPlaying {title, artist, playing, thumbnailBase64}
ReceiveHomeAssistantState {connected, entities: {id: {state, name, unit, brightness, color}}}: entidades usadas nos botões
ReceiveSpotifyState {connected, account, playing, track, artist, liked, shuffle, repeat, volume, device, coverBase64}
ReceiveTwitchState, ReceiveYouTubeState {connected, account, live, viewers, title}

Ao conectar, o novo cliente recebe o estado atual do Discord, do layout, do OBS e da Tuya.

Descoberta: UDP 7573

Envie o texto ASCII DroidDeckDiscoveryRequest em broadcast para a porta 7573. O servidor responde com {"ip": "...", "name": "<machine name>"}. A descoberta nunca retorna a chave de API.

URI de pareamento

droiddeck://pair?ip=<ip>&port=4787&key=<key>