commit cda74c3d6011614191641f7080a6e1a9faac3815 Author: Thomas Klaehn Date: Wed Mar 1 07:45:34 2023 +0100 Initial commit diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..19a5929 --- /dev/null +++ b/go.mod @@ -0,0 +1,11 @@ +module git.blackfinn.de/apiservice/sauna + +go 1.20 + +require github.com/eclipse/paho.mqtt.golang v1.4.2 + +require ( + github.com/gorilla/websocket v1.4.2 // indirect + golang.org/x/net v0.0.0-20200425230154-ff2c4b7c35a0 // indirect + golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..ddd8ce6 --- /dev/null +++ b/go.sum @@ -0,0 +1,12 @@ +github.com/eclipse/paho.mqtt.golang v1.4.2 h1:66wOzfUHSSI1zamx7jR6yMEI5EuHnT1G6rNA5PM12m4= +github.com/eclipse/paho.mqtt.golang v1.4.2/go.mod h1:JGt0RsEwEX+Xa/agj90YJ9d9DH2b7upDZMK9HRbFvCA= +github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/net v0.0.0-20200425230154-ff2c4b7c35a0 h1:Jcxah/M+oLZ/R4/z5RzfPzGbPXnVDPkEDtf2JnuxN+U= +golang.org/x/net v0.0.0-20200425230154-ff2c4b7c35a0/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/sauna.go b/sauna.go new file mode 100644 index 0000000..0c542f4 --- /dev/null +++ b/sauna.go @@ -0,0 +1,100 @@ +package sauna + +import ( + "encoding/json" + "log" + "math/rand" + "net/http" + "strconv" + "sync" + "time" + + mqtt "github.com/eclipse/paho.mqtt.golang" +) + +type temperature struct { + Value float64 `json:"value"` + Unit string `json:"unit"` + Time time.Time `json:"time"` + Valid bool `json:"valid"` +} + +var ( + logger log.Logger = *log.Default() + sauna_mutex sync.Mutex + sauna_temperature = temperature{ + Value: 0.0, + Unit: "°C", + } +) + +var messagePubHandler mqtt.MessageHandler = func(client mqtt.Client, msg mqtt.Message) { + logger.Printf("Received message: %s from topic: %s\n", msg.Payload(), msg.Topic()) +} + +var saunaHandler mqtt.MessageHandler = func(client mqtt.Client, msg mqtt.Message) { + log.Printf("Received message: %s from topic: %s\n", msg.Payload(), msg.Topic()) + sauna_mutex.Lock() + err := json.Unmarshal(msg.Payload(), &sauna_temperature) + sauna_temperature.Time = time.Now() + if err != nil { + sauna_temperature.Valid = false + sauna_mutex.Unlock() + logger.Print(err) + return + } + sauna_temperature.Valid = true + sauna_mutex.Unlock() +} + +var connectHandler mqtt.OnConnectHandler = func(client mqtt.Client) { + logger.Println("Connected") +} + +var connectLostHandler mqtt.ConnectionLostHandler = func(client mqtt.Client, err error) { + logger.Printf("Connect lost: %v", err) +} + +func init() { + logger.SetPrefix("apiservice/sauna: ") +} + +func http_endpoint_sauna(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-type", "application/json; charset=utf-8;") + if r.Method == http.MethodGet { + sauna_mutex.Lock() + data, err := json.Marshal(sauna_temperature) + sauna_mutex.Unlock() + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + w.Write(json.RawMessage(`{"error": "cannot marshal object to json"}`)) + } else { + w.WriteHeader(http.StatusOK) + w.Write(data) + } + } else { + w.WriteHeader(http.StatusMethodNotAllowed) + } +} + +func Start() { + // MQTT connection + opts := mqtt.NewClientOptions() + opts.AddBroker("tcp://nuc:1883") + opts.SetClientID("apiservice/sauna/" + strconv.Itoa(rand.Int())) + opts.SetDefaultPublishHandler(messagePubHandler) + opts.OnConnect = connectHandler + opts.OnConnectionLost = connectLostHandler + client := mqtt.NewClient(opts) + if token := client.Connect(); token.Wait() && token.Error() != nil { + panic(token.Error()) + } + + // MQTT subscribtion + topic := "sauna/temperature" + token := client.Subscribe(topic, 1, saunaHandler) + token.Wait() + logger.Printf("Subscribed to topic %s", topic) + + http.HandleFunc("/api/sauna", http_endpoint_sauna) +}