<!doctype html>
<html lang="de">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>PiClock Remote</title>
  <style>
    :root {
      color-scheme: dark;
      --bg: #0b1220;
      --bg-soft: #111a2b;
      --panel: #16223a;
      --panel-soft: #1d2b46;
      --line: #2e4266;
      --text: #eef4ff;
      --muted: #9db0cf;
      --ok: #22c55e;
      --warn: #f59e0b;
      --err: #ef4444;
      --accent: #4f8cff;
    }
    * { box-sizing: border-box; }
    body {
      margin: 0;
      padding: 20px;
      background: radial-gradient(1200px 700px at 20% -10%, #1b2a4a 0%, var(--bg) 65%);
      color: var(--text);
      font: 14px/1.45 "Segoe UI", Arial, sans-serif;
    }
    .app {
      max-width: 1200px;
      margin: 0 auto;
      display: grid;
      gap: 14px;
    }
    .card {
      background: linear-gradient(180deg, var(--panel) 0%, var(--bg-soft) 100%);
      border: 1px solid var(--line);
      border-radius: 14px;
      padding: 14px;
      box-shadow: 0 12px 28px rgba(0, 0, 0, 0.22);
    }
    .topbar {
      display: flex;
      justify-content: space-between;
      align-items: center;
      gap: 10px;
      flex-wrap: wrap;
    }
    h1, h2, h3 {
      margin: 0 0 8px;
      font-weight: 700;
    }
    h1 { font-size: 1.25rem; }
    h2 { font-size: 1rem; color: #dce8ff; }
    h3 { font-size: 0.94rem; color: #dce8ff; }
    .muted { color: var(--muted); }
    .row {
      display: flex;
      flex-wrap: wrap;
      align-items: center;
      gap: 8px;
    }
    .status {
      display: inline-flex;
      align-items: center;
      gap: 8px;
      border-radius: 999px;
      padding: 6px 12px;
      border: 1px solid var(--line);
      background: rgba(255, 255, 255, 0.03);
      font-size: 0.85rem;
    }
    .dot {
      width: 8px;
      height: 8px;
      border-radius: 999px;
      background: #7f8ea8;
    }
    .status.connected .dot { background: var(--ok); }
    .status.connecting .dot { background: var(--warn); }
    .status.disconnected .dot { background: var(--err); }
    button, input, select {
      border-radius: 10px;
      border: 1px solid var(--line);
      background: var(--panel-soft);
      color: var(--text);
      padding: 8px 10px;
      font-size: 0.9rem;
    }
    button {
      cursor: pointer;
      font-weight: 600;
    }
    button:hover { border-color: var(--accent); }
    .primary { background: var(--accent); border-color: transparent; }
    .danger { background: var(--err); border-color: transparent; }
    .grid {
      display: grid;
      grid-template-columns: 1fr;
      gap: 12px;
    }
    @media (min-width: 980px) {
      .grid {
        grid-template-columns: 1.1fr 1fr;
      }
    }
    .device-grid {
      display: grid;
      grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
      gap: 10px;
    }
    .device {
      border: 1px solid var(--line);
      border-radius: 12px;
      padding: 11px;
      background: rgba(255, 255, 255, 0.02);
      cursor: pointer;
    }
    .device.active {
      border-color: var(--accent);
      box-shadow: inset 0 0 0 1px rgba(79, 140, 255, 0.25);
    }
    .pill {
      border: 1px solid var(--line);
      border-radius: 999px;
      padding: 2px 8px;
      font-size: 0.72rem;
      color: var(--muted);
    }
    .pill.ok { color: #9cf0bf; border-color: rgba(34, 197, 94, 0.5); }
    .pill.err { color: #ffb0b0; border-color: rgba(239, 68, 68, 0.5); }
    table {
      width: 100%;
      border-collapse: collapse;
      font-size: 0.88rem;
    }
    th, td {
      text-align: left;
      border-bottom: 1px solid var(--line);
      padding: 7px 5px;
      vertical-align: top;
    }
    .mono { font-family: Consolas, monospace; }
    .list {
      max-height: 220px;
      overflow: auto;
      border: 1px solid var(--line);
      border-radius: 10px;
      padding: 8px;
      background: rgba(0, 0, 0, 0.2);
    }
    .list pre {
      margin: 0;
      white-space: pre-wrap;
      font-family: Consolas, monospace;
      font-size: 0.82rem;
    }
    #log {
      margin: 0;
      max-height: 180px;
      overflow: auto;
      border: 1px solid var(--line);
      border-radius: 10px;
      padding: 8px;
      background: rgba(0, 0, 0, 0.26);
      font-family: Consolas, monospace;
      font-size: 0.8rem;
      white-space: pre-wrap;
    }
  </style>
</head>
<body>
  <main class="app">
    <section class="card">
      <div class="topbar">
        <div>
          <h1>PiClock Remote</h1>
          <div class="muted">Modernes Dashboard zur Wecker- und Audio-Steuerung</div>
        </div>
        <div class="row">
          <span id="connState" class="status disconnected"><span class="dot"></span><span id="connText">Getrennt</span></span>
          <button id="btnRefresh">Aktualisieren</button>
          <button id="btnReconnect" class="primary">Neu verbinden</button>
        </div>
      </div>
    </section>

    <section class="card">
      <h2>Geräte</h2>
      <div id="devicesGrid" class="device-grid"><div class="muted">Noch keine Wecker gemeldet.</div></div>
    </section>

    <section class="card">
      <div id="deviceHint" class="muted">Gerät auswählen, um Details und Steuerung zu öffnen.</div>
      <div id="deviceDetail" style="display:none;">
        <div class="row" style="justify-content:space-between; margin-bottom:8px;">
          <h2 id="selectedDeviceName">Gerät</h2>
          <a id="openUiLink" href="#" target="_blank" rel="noopener noreferrer" class="muted">Lokale UI öffnen</a>
        </div>

        <div class="grid">
          <div class="card" style="padding:10px;">
            <h3>Wecker</h3>
            <div class="row" style="margin-bottom:8px;">
              <button id="btnStop" class="danger">Alarm stoppen</button>
              <label>Snooze
                <input id="snoozeMinutes" type="number" min="1" max="60" value="5" style="width:72px;">
              </label>
              <button id="btnSnooze">Senden</button>
            </div>
            <div id="alarmInfo" class="muted">Noch keine Daten.</div>

            <div class="card" style="padding:10px; margin-top:10px;">
              <h3>Quick Wecker speichern</h3>
              <div class="row">
                <label>ID <input id="formAlarmId" type="number" min="0" style="width:74px;"></label>
                <label>Zeit <input id="formAlarmZeit" type="time" value="07:00"></label>
                <label>Wiederholung
                  <select id="formAlarmRepeat">
                    <option value="taeglich">Täglich</option>
                    <option value="einmal">Einmal</option>
                    <option value="woechentlich">Wöchentlich</option>
                  </select>
                </label>
                <label>Text <input id="formAlarmText" type="text" value="Guten Morgen!"></label>
                <button id="btnSaveAlarm" class="primary">Speichern</button>
                <button id="btnNewAlarm">Neu</button>
              </div>
            </div>

            <table style="margin-top:10px;">
              <thead><tr><th>ID</th><th>Zeit</th><th>Wiederholung</th><th>Status</th><th>Aktion</th></tr></thead>
              <tbody id="alarmTable"><tr><td colspan="5" class="muted">Keine Daten.</td></tr></tbody>
            </table>
          </div>

          <div class="card" style="padding:10px;">
            <h3>Audio & Radio</h3>
            <div id="audioState" class="muted" style="margin-bottom:8px;">Audio-Status: unbekannt</div>
            <div class="row" style="margin-bottom:8px;">
              <button id="btnAudioPrev">◀◀</button>
              <button id="btnAudioPlayPause">Play/Pause</button>
              <button id="btnAudioNext">▶▶</button>
              <label>Vol <input id="audioVolume" type="number" min="0" max="100" value="80" style="width:72px;"></label>
              <button id="btnAudioVolume">Setzen</button>
            </div>
            <div class="row" style="margin-bottom:8px;">
              <button id="btnLoadMusic">Musik laden</button>
              <button id="btnLoadRadio">Radio laden</button>
            </div>
            <div class="row" style="margin-bottom:8px;">
              <label>Track
                <select id="audioTrackSelect" style="min-width:240px;"></select>
              </label>
              <button id="btnPlayTrack">Track starten</button>
            </div>
            <div class="row" style="margin-bottom:8px;">
              <label>Radio
                <select id="radioSelect" style="min-width:240px;"></select>
              </label>
              <button id="btnPlayRadio">Radio starten</button>
            </div>
            <div class="list" id="musicList"><pre class="muted">Noch keine Musikdaten geladen.</pre></div>
          </div>
        </div>
      </div>
    </section>

    <section class="card">
      <h2>Log</h2>
      <pre id="log"></pre>
    </section>
  </main>

  <script>
    const WS_URL = `${location.protocol === 'https:' ? 'wss:' : 'ws:'}//njs.piclock.cylone-network.com/ws`;
    const els = {
      connState: document.getElementById('connState'),
      connText: document.getElementById('connText'),
      btnRefresh: document.getElementById('btnRefresh'),
      btnReconnect: document.getElementById('btnReconnect'),
      devicesGrid: document.getElementById('devicesGrid'),
      deviceHint: document.getElementById('deviceHint'),
      deviceDetail: document.getElementById('deviceDetail'),
      selectedDeviceName: document.getElementById('selectedDeviceName'),
      openUiLink: document.getElementById('openUiLink'),
      alarmInfo: document.getElementById('alarmInfo'),
      alarmTable: document.getElementById('alarmTable'),
      btnStop: document.getElementById('btnStop'),
      snoozeMinutes: document.getElementById('snoozeMinutes'),
      btnSnooze: document.getElementById('btnSnooze'),
      formAlarmId: document.getElementById('formAlarmId'),
      formAlarmZeit: document.getElementById('formAlarmZeit'),
      formAlarmRepeat: document.getElementById('formAlarmRepeat'),
      formAlarmText: document.getElementById('formAlarmText'),
      btnSaveAlarm: document.getElementById('btnSaveAlarm'),
      btnNewAlarm: document.getElementById('btnNewAlarm'),
      audioState: document.getElementById('audioState'),
      btnAudioPrev: document.getElementById('btnAudioPrev'),
      btnAudioPlayPause: document.getElementById('btnAudioPlayPause'),
      btnAudioNext: document.getElementById('btnAudioNext'),
      audioVolume: document.getElementById('audioVolume'),
      btnAudioVolume: document.getElementById('btnAudioVolume'),
      btnLoadMusic: document.getElementById('btnLoadMusic'),
      btnLoadRadio: document.getElementById('btnLoadRadio'),
      audioTrackSelect: document.getElementById('audioTrackSelect'),
      btnPlayTrack: document.getElementById('btnPlayTrack'),
      radioSelect: document.getElementById('radioSelect'),
      btnPlayRadio: document.getElementById('btnPlayRadio'),
      musicList: document.getElementById('musicList'),
      log: document.getElementById('log'),
    };

    const state = {
      socket: null,
      reconnectTimer: null,
      manualDisconnect: false,
      devices: [],
      selectedDeviceId: null,
      deviceStateById: {},
      musicByDeviceId: {},
      radioByDeviceId: {},
      audioStateByDeviceId: {},
      editingAlarmId: 0,
      alarmFormDirty: false,
      pendingEditAlarmId: 0,
    };

    function esc(v) {
      return String(v ?? '').replaceAll('&', '&amp;').replaceAll('<', '&lt;').replaceAll('>', '&gt;').replaceAll('"', '&quot;').replaceAll("'", '&#039;');
    }
    function log(msg) {
      const ts = new Date().toLocaleTimeString('de-DE');
      els.log.textContent = `[${ts}] ${msg}\n${els.log.textContent}`;
    }
    function setConn(mode, text) {
      els.connState.className = `status ${mode}`;
      els.connText.textContent = text;
    }
    function send(type, payload = {}) {
      if (!state.socket || state.socket.readyState !== WebSocket.OPEN) return;
      state.socket.send(JSON.stringify({ type, ...payload }));
    }
    function selectedDevice() {
      return state.devices.find((d) => d.id === state.selectedDeviceId) || null;
    }

    function requestSelectedState() {
      if (!state.selectedDeviceId) return;
      send('get_device_state', { deviceId: state.selectedDeviceId });
      send('audio_get_state', { deviceId: state.selectedDeviceId });
      send('music_list', { deviceId: state.selectedDeviceId });
      send('radio_list', { deviceId: state.selectedDeviceId });
    }

    function resetAlarmForm() {
      state.editingAlarmId = 0;
      state.pendingEditAlarmId = 0;
      state.alarmFormDirty = false;
      els.formAlarmId.value = '';
      els.formAlarmZeit.value = '07:00';
      els.formAlarmRepeat.value = 'taeglich';
      els.formAlarmText.value = 'Guten Morgen!';
      els.btnSaveAlarm.textContent = 'Speichern';
    }

    function fillAlarmForm(alarm, options = {}) {
      if (!alarm) return;
      const id = Number(alarm.id || 0);
      const force = !!options.force;
      if (!force && state.alarmFormDirty && state.editingAlarmId === id) {
        log(`Bearbeitung von Wecker ${id} aktiv, Felder bleiben unverändert.`);
        return;
      }
      state.editingAlarmId = id;
      state.alarmFormDirty = false;
      els.formAlarmId.value = id > 0 ? String(id) : '';
      els.formAlarmZeit.value = String(alarm.weckzeit || '').slice(0, 5) || '07:00';
      els.formAlarmRepeat.value = String(alarm.wiederholung || 'taeglich');
      els.formAlarmText.value = String(alarm.sprachtext || alarm.sprachetext || alarm.text || '');
      if (!els.formAlarmText.value) els.formAlarmText.value = 'Guten Morgen!';
      els.btnSaveAlarm.textContent = `Wecker ${id || ''} aktualisieren`;
    }

    function renderDevices() {
      if (!state.devices.length) {
        els.devicesGrid.innerHTML = '<div class="muted">Noch keine Wecker gemeldet.</div>';
        els.deviceHint.style.display = 'block';
        els.deviceDetail.style.display = 'none';
        return;
      }
      if (!state.selectedDeviceId || !state.devices.some((d) => d.id === state.selectedDeviceId)) {
        state.selectedDeviceId = state.devices[0].id;
      }
      els.devicesGrid.innerHTML = state.devices.map((d) => {
        const active = d.id === state.selectedDeviceId ? ' active' : '';
        const onlineCls = d.online ? 'ok' : 'err';
        const onlineTxt = d.online ? 'online' : 'offline';
        return `<article class="device${active}" data-id="${esc(d.id)}">
          <div class="row" style="justify-content:space-between;">
            <strong>${esc(d.name || d.id)}</strong><span class="pill ${onlineCls}">${onlineTxt}</span>
          </div>
          <div class="muted mono">${esc(d.id)}</div>
        </article>`;
      }).join('');
      els.devicesGrid.querySelectorAll('[data-id]').forEach((el) => {
        el.addEventListener('click', () => {
          state.selectedDeviceId = el.getAttribute('data-id');
          renderDevices();
          renderSelected();
          requestSelectedState();
        });
      });
      renderSelected();
    }

    function renderAlarms() {
      const dev = selectedDevice();
      const alarms = Array.isArray(state.deviceStateById[dev?.id]?.list?.wecker) ? state.deviceStateById[dev.id].list.wecker : [];
      if (!alarms.length) {
        els.alarmTable.innerHTML = '<tr><td colspan="5" class="muted">Keine Wecker.</td></tr>';
        return;
      }
      els.alarmTable.innerHTML = alarms.map((a) => {
        const id = Number(a.id || 0);
        const active = Number(a.aktiv || 0) === 1;
        const next = active ? 0 : 1;
        return `<tr>
          <td class="mono">${id}</td><td class="mono">${esc(String(a.weckzeit || '').slice(0, 5) || '--:--')}</td>
          <td>${esc(a.wiederholung || '-')}</td><td>${active ? 'Aktiv' : 'Inaktiv'}</td>
          <td class="row">
            <button data-toggle-id="${id}" data-toggle-active="${next}">${active ? 'Deaktivieren' : 'Aktivieren'}</button>
            <button data-edit-id="${id}">Bearbeiten</button>
          </td>
        </tr>`;
      }).join('');
      els.alarmTable.querySelectorAll('button[data-toggle-id]').forEach((btn) => {
        btn.addEventListener('click', () => {
          if (!state.selectedDeviceId) return;
          send('toggle', {
            deviceId: state.selectedDeviceId,
            id: Number(btn.getAttribute('data-toggle-id')),
            aktiv: Number(btn.getAttribute('data-toggle-active')) === 1,
          });
        });
      });
      els.alarmTable.querySelectorAll('button[data-edit-id]').forEach((btn) => {
        btn.addEventListener('click', () => {
          if (!state.selectedDeviceId) return;
          const id = Number(btn.getAttribute('data-edit-id') || 0);
          if (id <= 0) return;
          state.pendingEditAlarmId = id;
          send('get_alarm', { deviceId: state.selectedDeviceId, id });
        });
      });
    }

    function renderAlarmState() {
      const dev = selectedDevice();
      const a = state.deviceStateById[dev?.id]?.alarmState;
      if (!a) {
        els.alarmInfo.textContent = 'Noch keine Daten.';
        return;
      }
      els.alarmInfo.innerHTML = `Klingelt: <strong>${a.ringing ? 'Ja' : 'Nein'}</strong> · ID: <span class="mono">${esc(a.alarm_id ?? '-')}</span> · Zeit: <span class="mono">${esc(a.alarm_time ?? '-')}</span> · TTS: ${esc(a.tts_text || '-')}`;
    }

    function renderMusic() {
      const dev = selectedDevice();
      const files = state.musicByDeviceId[dev?.id]?.music?.files || [];
      if (!files.length) {
        els.audioTrackSelect.innerHTML = '';
        els.musicList.innerHTML = '<pre class="muted">Noch keine Musikdaten geladen.</pre>';
        return;
      }
      els.audioTrackSelect.innerHTML = files.map((f, i) => `<option value="${i}">${esc(f.titel || f.name || 'Unbenannt')}</option>`).join('');
      const lines = files.slice(0, 40).map((f) => `- ${esc(f.titel || f.name || 'Unbenannt')}`).join('\n');
      els.musicList.innerHTML = `<pre>${lines}</pre>`;
    }

    function renderRadio() {
      const dev = selectedDevice();
      const stations = state.radioByDeviceId[dev?.id]?.radio?.stations || [];
      if (!stations.length) {
        els.radioSelect.innerHTML = '';
        return;
      }
      els.radioSelect.innerHTML = stations.slice(0, 300).map((s, i) => `<option value="${i}">${esc(s.name || `Sender ${i + 1}`)}</option>`).join('');
    }

    function renderAudioState() {
      const dev = selectedDevice();
      const a = state.audioStateByDeviceId[dev?.id] || state.deviceStateById[dev?.id]?.audioState;
      if (!a) {
        els.audioState.textContent = 'Audio-Status: unbekannt';
        return;
      }
      const title = a.currentTrack?.titel || a.currentTrack?.name || 'Kein Titel';
      const source = a.sourceType || 'lokal';
      const status = a.isPlaying ? 'spielt' : 'pausiert';
      const vol = Number(a.volume ?? 0);
      els.audioVolume.value = String(Math.max(0, Math.min(100, vol)));
      els.audioState.textContent = `Audio: ${status} · ${source} · ${title} · Vol ${vol}%`;
    }

    function renderSelected() {
      const d = selectedDevice();
      if (!d) {
        els.deviceHint.style.display = 'block';
        els.deviceDetail.style.display = 'none';
        return;
      }
      els.deviceHint.style.display = 'none';
      els.deviceDetail.style.display = 'block';
      els.selectedDeviceName.textContent = `${d.name || d.id} (${d.id})`;
      els.openUiLink.href = d.uiUrl || '#';
      renderAlarmState();
      renderAlarms();
      renderMusic();
      renderRadio();
      renderAudioState();
    }

    function connect() {
      if (state.socket && state.socket.readyState === WebSocket.OPEN) {
        state.manualDisconnect = true;
        state.socket.close();
      }
      setConn('connecting', 'Verbinde...');
      log(`Verbinde zu ${WS_URL}`);
      const ws = new WebSocket(WS_URL);
      state.socket = ws;
      state.manualDisconnect = false;
      ws.addEventListener('open', () => {
        setConn('connected', 'Verbunden');
        log('Verbindung hergestellt.');
        send('get_devices');
      });
      ws.addEventListener('message', (evt) => {
        let msg = null;
        try { msg = JSON.parse(evt.data); } catch { return; }
        if (msg.type === 'devices') {
          state.devices = Array.isArray(msg.payload?.devices) ? msg.payload.devices : [];
          renderDevices();
          return;
        }
        if (msg.type === 'device_state') {
          const devId = msg.payload?.device?.id || state.selectedDeviceId;
          if (devId) {
            state.deviceStateById[devId] = msg.payload;
            if (msg.payload?.audioState) state.audioStateByDeviceId[devId] = msg.payload.audioState;
            renderSelected();
          }
          return;
        }
        if (msg.type === 'device_music') {
          const devId = msg.payload?.deviceId;
          if (devId) {
            state.musicByDeviceId[devId] = msg.payload;
            renderMusic();
          }
          return;
        }
        if (msg.type === 'device_alarm') {
          const alarm = msg.payload?.alarm || null;
          if (alarm) {
            const alarmId = Number(alarm.id || 0);
            const force = state.pendingEditAlarmId > 0 && state.pendingEditAlarmId === alarmId;
            fillAlarmForm(alarm, { force });
            if (force) state.pendingEditAlarmId = 0;
            log(`Wecker ${alarm.id || ''} geladen.`);
          } else {
            log('Wecker konnte nicht geladen werden.');
          }
          return;
        }
        if (msg.type === 'device_radio') {
          const devId = msg.payload?.deviceId;
          if (devId) {
            state.radioByDeviceId[devId] = msg.payload;
            renderRadio();
          }
          return;
        }
        if (msg.type === 'device_audio_state') {
          const devId = msg.payload?.deviceId;
          if (devId) {
            state.audioStateByDeviceId[devId] = msg.payload.audioState || null;
            renderAudioState();
          }
          return;
        }
        if (msg.type === 'action_result') {
          log(`Aktion "${msg.action}" erfolgreich${msg.deviceId ? ` auf ${msg.deviceId}` : ''}.`);
          return;
        }
        if (msg.type === 'error') {
          log(`Fehler: ${msg.message || 'unbekannt'}`);
        }
      });
      ws.addEventListener('close', () => {
        setConn('disconnected', 'Getrennt');
        log('Verbindung geschlossen.');
        if (!state.manualDisconnect) {
          clearTimeout(state.reconnectTimer);
          state.reconnectTimer = setTimeout(connect, 2000);
        }
      });
      ws.addEventListener('error', () => log('WebSocket Fehler.'));
    }

    els.btnReconnect.addEventListener('click', connect);
    els.btnRefresh.addEventListener('click', () => {
      send('get_devices');
      requestSelectedState();
    });
    els.btnStop.addEventListener('click', () => state.selectedDeviceId && send('stop_alarm', { deviceId: state.selectedDeviceId }));
    els.btnSnooze.addEventListener('click', () => state.selectedDeviceId && send('snooze', { deviceId: state.selectedDeviceId, minutes: Number(els.snoozeMinutes.value || 5) }));
    els.btnSaveAlarm.addEventListener('click', () => {
      if (!state.selectedDeviceId) return;
      const formId = Number(els.formAlarmId.value || state.editingAlarmId || 0);
      const weckzeit = String(els.formAlarmZeit.value || '').trim();
      const sprachtext = String(els.formAlarmText.value || '').trim();
      if (!weckzeit || !sprachtext) return log('Bitte Uhrzeit und Text angeben.');
      state.alarmFormDirty = false;
      send('save_alarm', {
        deviceId: state.selectedDeviceId,
        id: formId,
        weckzeit,
        wiederholung: String(els.formAlarmRepeat.value || 'taeglich'),
        sprachtext,
        aktiv: true,
      });
    });
    els.btnNewAlarm.addEventListener('click', resetAlarmForm);
    [els.formAlarmId, els.formAlarmZeit, els.formAlarmRepeat, els.formAlarmText].forEach((input) => {
      if (!input) return;
      input.addEventListener('input', () => {
        state.alarmFormDirty = true;
      });
      input.addEventListener('change', () => {
        state.alarmFormDirty = true;
      });
    });
    els.btnLoadMusic.addEventListener('click', () => state.selectedDeviceId && send('music_list', { deviceId: state.selectedDeviceId }));
    els.btnLoadRadio.addEventListener('click', () => state.selectedDeviceId && send('radio_list', { deviceId: state.selectedDeviceId }));
    els.btnAudioPrev.addEventListener('click', () => state.selectedDeviceId && send('audio_prev', { deviceId: state.selectedDeviceId }));
    els.btnAudioPlayPause.addEventListener('click', () => state.selectedDeviceId && send('audio_play_pause', { deviceId: state.selectedDeviceId }));
    els.btnAudioNext.addEventListener('click', () => state.selectedDeviceId && send('audio_next', { deviceId: state.selectedDeviceId }));
    els.btnAudioVolume.addEventListener('click', () => state.selectedDeviceId && send('audio_set_volume', { deviceId: state.selectedDeviceId, volume: Number(els.audioVolume.value || 0) }));
    els.btnPlayTrack.addEventListener('click', () => {
      if (!state.selectedDeviceId) return;
      const files = state.musicByDeviceId[state.selectedDeviceId]?.music?.files || [];
      const index = Number(els.audioTrackSelect.value || 0);
      const track = files[index] || null;
      send('audio_play_track', { deviceId: state.selectedDeviceId, index, name: track?.name || '' });
    });
    els.btnPlayRadio.addEventListener('click', () => {
      if (!state.selectedDeviceId) return;
      const stations = state.radioByDeviceId[state.selectedDeviceId]?.radio?.stations || [];
      const index = Number(els.radioSelect.value || 0);
      const station = stations[index] || null;
      send('audio_play_radio', { deviceId: state.selectedDeviceId, index, stationName: station?.name || '' });
    });

    connect();
    resetAlarmForm();
  </script>
</body>
</html>
