8.9 KiB · text History 65cab78
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="stylesheet" href="/app.css">
  <title>Device Setup</title>
  <script>
    // Theme bootstrap. Runs before paint to avoid a flash of the wrong theme.
    (() => {
      const SYSTEM_DARK = "afterdark";
      const SYSTEM_LIGHT = "her";
      const mq = window.matchMedia("(prefers-color-scheme: dark)");
      const systemTheme = () => (mq.matches ? SYSTEM_DARK : SYSTEM_LIGHT);
      window.portalSetTheme = (theme) => {
        if (theme === "system") {
          localStorage.removeItem("portal:theme");
          document.documentElement.setAttribute("data-theme", systemTheme());
        } else {
          localStorage.setItem("portal:theme", theme);
          document.documentElement.setAttribute("data-theme", theme);
        }
      };
      if (!document.documentElement.hasAttribute("data-theme")) {
        window.portalSetTheme(localStorage.getItem("portal:theme") || "system");
      }
      mq.addEventListener("change", () => {
        if (!localStorage.getItem("portal:theme")) window.portalSetTheme("system");
      });
    })();
  </script>
</head>
<body class="min-h-screen bg-base-100 text-base-content">
  <header class="navbar bg-base-200 border-b border-base-300 px-4 sm:px-6">
    <div class="flex-1">
      <span class="font-bold tracking-[0.2em] text-accent"><span id="brand-root"></span>DEVICE&nbsp;SETUP</span>
    </div>
    <div class="flex-none">
      <div class="dropdown dropdown-end">
        <div tabindex="0" role="button" class="btn btn-ghost btn-sm gap-2" aria-label="Choose theme">
          <svg xmlns="http://www.w3.org/2000/svg" class="size-4" viewBox="0 0 24 24" fill="none"
               stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
            <circle cx="13.5" cy="6.5" r=".5" fill="currentColor"/>
            <circle cx="17.5" cy="10.5" r=".5" fill="currentColor"/>
            <circle cx="8.5" cy="7.5" r=".5" fill="currentColor"/>
            <circle cx="6.5" cy="12.5" r=".5" fill="currentColor"/>
            <path d="M12 2C6.5 2 2 6.5 2 12s4.5 10 10 10c.926 0 1.648-.746 1.648-1.688 0-.437-.18-.835-.437-1.125-.29-.289-.438-.652-.438-1.125a1.64 1.64 0 0 1 1.668-1.668h1.996c3.051 0 5.555-2.503 5.555-5.554C21.965 6.012 17.461 2 12 2z"/>
          </svg>
          <span class="hidden sm:inline">Theme</span>
        </div>
        <ul tabindex="0" class="dropdown-content menu bg-base-200 border border-base-300 rounded-box z-10 mt-1 w-44 p-1 shadow-lg">
          <li><button type="button" onclick="portalSetTheme('system')">System</button></li>
          <li>
            <button type="button" onclick="portalSetTheme('her')" class="flex items-center justify-between">
              <span>Her</span>
              <span class="size-3 rounded-full border border-base-content/20" data-theme="her" style="background: var(--color-primary)"></span>
            </button>
          </li>
          <li>
            <button type="button" onclick="portalSetTheme('afterdark')" class="flex items-center justify-between">
              <span>Afterdark</span>
              <span class="size-3 rounded-full border border-base-content/20" data-theme="afterdark" style="background: var(--color-primary)"></span>
            </button>
          </li>
        </ul>
      </div>
    </div>
  </header>

  <main class="px-4 py-10 sm:px-6">
    <div class="mx-auto max-w-md space-y-4">
      <div class="card bg-base-200 border border-base-300 shadow-lg">
        <div class="card-body gap-4">
          <h1 class="card-title text-accent">Network Setup</h1>
          <form method="post" action="/save" class="space-y-6">

            <section class="space-y-3">
              <h2 class="text-xs font-bold uppercase tracking-wider text-secondary">Wi-Fi</h2>
              <label class="flex flex-col w-full">
                <span class="mb-1 text-sm text-base-content">Network (SSID)</span>
                <input type="text" name="wifi_ssid" class="input w-full"
                       autocapitalize="off" autocorrect="off" required>
              </label>
              <label class="flex flex-col w-full">
                <span class="mb-1 text-sm text-base-content">Password</span>
                <input type="password" name="wifi_pass" class="input w-full">
              </label>
              <p class="text-xs opacity-60">Leave the password blank for an open network.</p>

              <details class="rounded-lg border border-base-300 bg-base-100 px-3 py-2">
                <summary class="cursor-pointer text-xs font-semibold uppercase tracking-wider text-secondary">
                  Other networks
                </summary>
                <p class="mt-2 text-xs opacity-60">
                  Optional. The device joins whichever of these is in range, strongest first,
                  so it can be moved without being set up again.
                </p>
                <div class="mt-3 space-y-3">
                  <label class="flex flex-col w-full">
                    <span class="mb-1 text-sm text-base-content">Network 2 (SSID)</span>
                    <input type="text" name="wifi_ssid_2" class="input input-sm w-full"
                           autocapitalize="off" autocorrect="off">
                  </label>
                  <label class="flex flex-col w-full">
                    <span class="mb-1 text-sm text-base-content">Password 2</span>
                    <input type="password" name="wifi_pass_2" class="input input-sm w-full">
                  </label>
                  <label class="flex flex-col w-full">
                    <span class="mb-1 text-sm text-base-content">Network 3 (SSID)</span>
                    <input type="text" name="wifi_ssid_3" class="input input-sm w-full"
                           autocapitalize="off" autocorrect="off">
                  </label>
                  <label class="flex flex-col w-full">
                    <span class="mb-1 text-sm text-base-content">Password 3</span>
                    <input type="password" name="wifi_pass_3" class="input input-sm w-full">
                  </label>
                </div>
              </details>
            </section>

            <section class="space-y-3">
              <h2 class="text-xs font-bold uppercase tracking-wider text-secondary">MQTT Broker</h2>
              <div class="flex gap-3">
                <label class="flex flex-col grow">
                  <span class="mb-1 text-sm text-base-content">Host</span>
                  <input type="text" name="mqtt_host" class="input w-full"
                         placeholder="192.168.1.10" required>
                </label>
                <label class="flex flex-col w-24">
                  <span class="mb-1 text-sm text-base-content">Port</span>
                  <input type="number" name="mqtt_port" value="1883" min="1" max="65535"
                         class="input w-full">
                </label>
              </div>
              <label class="flex flex-col w-full">
                <span class="mb-1 text-sm text-base-content">Username</span>
                <input type="text" name="mqtt_username" class="input w-full" autocapitalize="off">
              </label>
              <label class="flex flex-col w-full">
                <span class="mb-1 text-sm text-base-content">Password</span>
                <input type="password" name="mqtt_password" class="input w-full">
              </label>
              <label class="flex cursor-pointer select-none items-center justify-start gap-3 px-1">
                <input type="checkbox" name="mqtt_use_tls" class="checkbox checkbox-primary checkbox-sm">
                <span class="text-sm text-base-content">Use TLS (mqtts)</span>
              </label>
            </section>

            <section class="space-y-3">
              <h2 class="text-xs font-bold uppercase tracking-wider text-secondary">Topic</h2>
              <label class="flex flex-col w-full">
                <span class="mb-1 text-sm text-base-content">Root prefix</span>
                <input type="text" name="topic_root" id="topic_root" value="neiam" class="input w-full">
              </label>
              <p class="text-xs opacity-60">
                Published topics are built under <code>&lt;root&gt;/&hellip;</code>.
              </p>
            </section>

            <button type="submit" class="btn btn-primary w-full">Save &amp; Reboot</button>
          </form>
        </div>
      </div>
    </div>
  </main>
  <script>
    // Prefix the navbar brand with the topic root, e.g. "CBZ DEVICE SETUP",
    // and keep it in sync as the field is edited.
    (() => {
      const input = document.getElementById('topic_root');
      const brand = document.getElementById('brand-root');
      const sync = () => {
        const root = input.value.trim();
        brand.textContent = root ? root.toUpperCase() + ' ' : '';
      };
      input.addEventListener('input', sync);
      sync();
    })();
  </script>
</body>
</html>