みかづきブログ・カスタム

基本的にはちょちょいのほいです。

Goを使ってウェブサーバを立て、POSTリクエストで電球の明るさを制御する 💡

前回は、ブラウザ上のボタンを押した際に1秒間電球を光らせてみました。

blog.kimizuka.org

今回は、POSTリクエストの際に値を送って、その値に応じた明るさに電球を光らせてみます。

リポジトリ

github.com

ソースコード

go.mod

module github.com/kimizuka/shining-go

go 1.24.3

require github.com/akualab/dmx v0.0.0-20130922234952-1ec6837faba7

require github.com/tarm/goserial v0.0.0-20151007205400-b3440c3c6355 // indirect

go.sum

github.com/akualab/dmx v0.0.0-20130922234952-1ec6837faba7 h1:NiSujjEOEg6lSdiIftZoHa0oAsDTZlabXrU7Lfa5jyw=
github.com/akualab/dmx v0.0.0-20130922234952-1ec6837faba7/go.mod h1:fGkOc9nHK/v2N4kXLbb+uet+BC9TiqI7srodczLFMnQ=
github.com/tarm/goserial v0.0.0-20151007205400-b3440c3c6355 h1:Kp3kg8YL2dc75mckomrHZQTfzNyFGnaqFhJeQw4ozGc=
github.com/tarm/goserial v0.0.0-20151007205400-b3440c3c6355/go.mod h1:jcMo2Odv5FpDA6rp8bnczbUolcICW6t54K3s9gOlgII=

main.go

package main

import (
  "encoding/json"
  "log"
  "net/http"

  "github.com/akualab/dmx"
)

func onLight(level int) {
  dmx, err := dmx.NewDMXConnection("/dev/tty.usbserial-EN437503") // ポートを合わせる
  if err != nil {
    log.Fatal(err)
  }

  dmx.SetChannel(2, byte(level)) // チャンネルを合わせる
  dmx.Render()
  dmx.Close()
}

func shiningGetHandler(w http.ResponseWriter, r *http.Request) {
  w.Header().Set("Content-Type", "application/json")

  switch r.Method {
  case http.MethodPost:
    var request struct {
      Level int `json:"level"`
    }
    if err := json.NewDecoder(r.Body).Decode(&request); err != nil {
      http.Error(w, "Invalid request body", http.StatusBadRequest)
      return
    }

    if request.Level < 0 || 100 < request.Level {
      http.Error(w, "Level must be between 0 and 100", http.StatusBadRequest)
      return
    }

    onLight(request.Level)

    response := struct {
      Level int `json:"level"`
    }{
      Level: request.Level,
    }

    json.NewEncoder(w).Encode(response)
  }
}

func main() {
  fs := http.FileServer(http.Dir("./public"))

  http.Handle("/", fs)
  http.HandleFunc("/api/shining", shiningGetHandler)

  port := ":8080"
  err := http.ListenAndServe(port, nil)
  if err != nil {
    log.Fatal(err)
  }
}

public/index.html

<html>
  <head>
    <meta charset="UTF-8">
    <title>Shining Go</title>
    <style>
      body {
        display: flex;
        align-items: center;
        justify-content: center;
        gap: 16px;
        position: fixed;
        inset: 0;
        transition: background .2s ease-in-out;

        &[data-level="0"] {
          background: #212121;
        }

        &[data-level="50"] {
          background: #BDBDBD;
        }

        &[data-level="100"] {
          background: #FAFAFA;
        }
      }

      button {
        width: 96px;
        height: 96px;
        font-size: 48px;
        cursor: pointer;
      }
    </style>
  </head>
  <body data-level="0">
    <button data-level="0">0</button>
    <button data-level="50">50</button>
    <button data-level="100">100</button>
    <script>
      [].slice.call(document.querySelectorAll('[data-level]')).forEach((elm) => {
        elm.addEventListener('click', (evt) => {
          const level = Number(evt.target.dataset.level || 0);

          fetch('/api/shining', {
            method: 'POST',
            headers: {
              'Content-Type': 'application/json'
            },
            body: JSON.stringify({ level })
          }).then(async (res) => {
            const json = await res.json();

            document.body.dataset.level = String(json.level);
          });
        });
      });
    </script>
  </body>
</html>

これで、OKです。
「0」で消灯。「100」で点灯。「50」でうっすら点灯という挙動になりました。
再びボタンを押すまでその状態を維持します。

DEMO

gifアニメで見ると、50の明るさと100の明るさの差がわかりにくいですが、人眼で見るとほんのりと差がわかります。