Alpha
WebSocket

Media Topic

Real-time media status and playback control via WebSocket

The media topic is the recommended way to build media widgets and remote controls. It provides real-time now-playing information and allows you to control playback directly through the same WebSocket connection.

Why WebSocket for Media?

Unlike the REST API, the WebSocket media topic: - Sends updates instantly when track, volume, or playback state changes - No polling required - zero wasted requests - Bidirectional - send commands and receive feedback through one connection - Efficient - Cntrl only broadcasts when something actually changes

Smart Updates

Media events only broadcast when something changes (track, play/pause, volume, mute). No constant polling - you only receive data when it matters.

Subscribe

{ "op": "subscribe", "data": { "topics": ["media"] } }

Event: media_update

Broadcasts immediately on subscribe, then whenever media state changes.

{
  "type": "media_update",
  "data": {
    "status": "playing",
    "volume": 75,
    "muted": false,
    "playing": true,
    "title": "Bohemian Rhapsody",
    "artist": "Queen",
    "supports_ctrl": true
  }
}

Field Reference

FieldTypeDescription
statusstring"playing", "paused", or "stopped"
volumeintSystem volume (0-100)
mutedboolWhether system audio is muted
playingboolTrue if media is actively playing
titlestringCurrent track title
artiststringCurrent artist name
supports_ctrlboolWhether playback control is supported

When Updates Are Sent

Updates trigger on any of these changes:

  • Track changes (new song)
  • Play/pause state changes
  • Mute/unmute
  • Volume changes

Volume Detection

Volume changes are detected and broadcast automatically. If you change volume using your keyboard or system controls, connected clients will see the update.

Sending Commands

Media Control

{
  "op": "media",
  "data": {
    "action": "play_pause"
  }
}

Available Actions

Playback

ActionDescription
playStart/resume playback
pausePause playback
play_pauseToggle play/pause
nextNext track
prevPrevious track

Volume

ActionValueDescription
volume_up-Increase volume
volume_down-Decrease volume
set_volume0-100Set specific volume level
mute-Mute audio
unmute-Unmute audio
toggle_mute-Toggle mute state

Set Volume Example

{
  "op": "media",
  "data": {
    "action": "set_volume",
    "value": 50
  }
}

Event: media_feedback

After sending a media command, you'll receive feedback:

{
  "type": "media_feedback",
  "data": {
    "success": true,
    "action": "play_pause",
    "message": null
  }
}

Feedback Fields

FieldTypeDescription
successboolWhether the command succeeded
actionstringThe action that was executed
messagestringError message if failed (null on success)

Two Messages

After a media command, you receive: 1. media_feedback - Immediate confirmation 2. media_update - State change (if the action changed something)

Configuration

In config.json:

{
  "websocket": {
    "media": {
      "enabled": true,
      "interval_ms": 500
    }
  }
}

The interval_ms controls how often the media state is polled for changes (not how often updates are sent).

Example Use Cases

1. Now Playing Widget

Display current track with play/pause control:

const ws = new WebSocket("ws://localhost:9990/api/ws");
let currentMedia = null;

ws.onopen = () => {
  ws.send(
    JSON.stringify({
      op: "subscribe",
      data: { topics: ["media"] },
    }),
  );
};

ws.onmessage = (event) => {
  const { type, data } = JSON.parse(event.data);

  if (type === "media_update") {
    currentMedia = data;
    document.getElementById("title").textContent = data.title || "Nothing playing";
    document.getElementById("artist").textContent = data.artist || "";
    document.getElementById("playBtn").textContent = data.playing ? "⏸" : "▶";
  }
};

function togglePlayPause() {
  ws.send(
    JSON.stringify({
      op: "media",
      data: { action: "play_pause" },
    }),
  );
}

2. Volume Slider

Real-time volume control with visual feedback:

const volumeSlider = document.getElementById("volume");

// Update slider when volume changes externally
ws.onmessage = (event) => {
  const { type, data } = JSON.parse(event.data);
  if (type === "media_update" && data.volume !== null) {
    volumeSlider.value = data.volume;
  }
};

// Send volume changes
volumeSlider.oninput = (e) => {
  ws.send(
    JSON.stringify({
      op: "media",
      data: { action: "set_volume", value: parseInt(e.target.value) },
    }),
  );
};

3. Smart Home Integration

Auto-pause when leaving home:

// Called by your smart home system
function onUserLeftHome() {
  ws.send(
    JSON.stringify({
      op: "media",
      data: { action: "pause" },
    }),
  );
}

// Auto-mute during meetings
function onMeetingStarted() {
  ws.send(
    JSON.stringify({
      op: "media",
      data: { action: "mute" },
    }),
  );
}

4. Media Remote Control

Full remote control for your PC:

const controls = {
  playPause: () => sendCommand("play_pause"),
  next: () => sendCommand("next"),
  prev: () => sendCommand("prev"),
  mute: () => sendCommand("toggle_mute"),
  volUp: () => sendCommand("volume_up"),
  volDown: () => sendCommand("volume_down"),
};

function sendCommand(action) {
  ws.send(
    JSON.stringify({
      op: "media",
      data: { action },
    }),
  );
}

// Bind to UI buttons
document.getElementById("playPauseBtn").onclick = controls.playPause;
document.getElementById("nextBtn").onclick = controls.next;
// ... etc

5. Track Change Notifications

Get notified when a new song starts:

let lastTitle = null;

ws.onmessage = (event) => {
  const { type, data } = JSON.parse(event.data);

  if (type === "media_update") {
    if (data.title && data.title !== lastTitle) {
      lastTitle = data.title;
      showNotification(`Now playing: ${data.title} - ${data.artist}`);
    }
  }
};

On this page