From 3df2537c8ddd5f2fc54498ebceed92efde1d30a3 Mon Sep 17 00:00:00 2001 From: Knut Ahlers Date: Fri, 6 Jul 2018 20:57:55 +0200 Subject: [PATCH] Initial version --- .gitignore | 1 + fs20.go | 46 +++++++++++++++++++++++++++ main.go | 91 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ mqtt.go | 17 ++++++++++ 4 files changed, 155 insertions(+) create mode 100644 .gitignore create mode 100644 fs20.go create mode 100644 main.go create mode 100644 mqtt.go diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d0f0cca --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +culmqtt diff --git a/fs20.go b/fs20.go new file mode 100644 index 0000000..ff6d795 --- /dev/null +++ b/fs20.go @@ -0,0 +1,46 @@ +package main + +import ( + "fmt" + "strings" + + mqtt "github.com/eclipse/paho.mqtt.golang" + log "github.com/sirupsen/logrus" +) + +func processFS20Message(housecode, device, command string) error { + log.WithFields(log.Fields{ + "housecode": housecode, + "device": device, + "command": command, + }).Info("FS20 status received") + + return publishFS20ToMQTT(housecode, device, command) +} + +func publishFS20ToCUL(client mqtt.Client, msg mqtt.Message) { + addr := strings.Split(msg.Topic(), "/")[1] + cmd := string(msg.Payload()) + + logger := log.WithFields(log.Fields{ + "address": addr, + "command": cmd, + }) + + if _, err := fmt.Fprintf(port, "F%s%s\n", addr, cmd); err != nil { + logger.WithError(err).Error("Unable to send message through CUL") + } + logger.Info("Message sent") +} + +func publishFS20ToMQTT(housecode, device, command string) error { + tok := brokerClient.Publish( + strings.Join([]string{"culmqtt", fmt.Sprintf("%s%s", housecode, device), "state"}, "/"), + 0x01, // QOS Level 1: At least once + true, + command, + ) + + tok.Wait() + return tok.Error() +} diff --git a/main.go b/main.go new file mode 100644 index 0000000..4100abc --- /dev/null +++ b/main.go @@ -0,0 +1,91 @@ +package main + +import ( + "bufio" + "fmt" + "io" + "os" + + "github.com/Luzifer/rconfig" + "github.com/jacobsa/go-serial/serial" + log "github.com/sirupsen/logrus" +) + +var ( + cfg = struct { + CULDevice string `flag:"cul-device" default:"/dev/ttyACM0" env:"CUL_DEVICE" description:"TTY of the CUL to connect to"` + LogLevel string `flag:"log-level" default:"info" description:"Log level (debug, info, warn, error, fatal)"` + MQTTHost string `flag:"mqtt-host" default:"tcp://127.0.0.1:1883" env:"MQTT_HOST" description:"Connection URI for the broker"` + MQTTUser string `flag:"mqtt-user" default:"" env:"MQTT_USER" description:"Username for broker connection"` + MQTTPass string `flag:"mqtt-pass" default:"" env:"MQTT_PASS" description:"Password for broker connection"` + VersionAndExit bool `flag:"version" default:"false" description:"Prints current version and exits"` + }{} + + port io.ReadWriteCloser + + version = "dev" +) + +func init() { + if err := rconfig.ParseAndValidate(&cfg); err != nil { + log.Fatalf("Unable to parse commandline options: %s", err) + } + + if cfg.VersionAndExit { + fmt.Printf("culmqtt %s\n", version) + os.Exit(0) + } + + if l, err := log.ParseLevel(cfg.LogLevel); err != nil { + log.WithError(err).Fatal("Unable to parse log level") + } else { + log.SetLevel(l) + } +} + +func main() { + options := serial.OpenOptions{ + PortName: cfg.CULDevice, + BaudRate: 19200, + DataBits: 8, + StopBits: 1, + MinimumReadSize: 4, + } + + // Open the port. + var err error + if port, err = serial.Open(options); err != nil { + log.Fatalf("serial.Open: %v", err) + } + + // Make sure to close it later. + defer port.Close() + + for { + scanner := bufio.NewScanner(port) + for scanner.Scan() { + if err := processMessage(scanner.Text()); err != nil { + log.WithError(err).Fatal("Unable to process message") + } + } + } +} + +func processMessage(message string) error { + logger := log.WithField("message", message) + + switch message[0] { + case 'F': + return processFS20Message( + message[1:5], // House code: 4 hex digits + message[5:7], // Device code: 2 hex digits + message[7:9], // Command: 2 hex digits + ) + case 'V': + // Version information, discard + return nil + default: + logger.Error("Unknown message specifier") + return nil + } +} diff --git a/mqtt.go b/mqtt.go new file mode 100644 index 0000000..95109d8 --- /dev/null +++ b/mqtt.go @@ -0,0 +1,17 @@ +package main + +import mqtt "github.com/eclipse/paho.mqtt.golang" + +var brokerClient mqtt.Client + +func init() { + opts := mqtt.NewClientOptions().AddBroker(cfg.MQTTHost) + if cfg.MQTTUser != "" || cfg.MQTTPass != "" { + opts.SetUsername(cfg.MQTTUser).SetPassword(cfg.MQTTPass) + } + + brokerClient = mqtt.NewClient(opts) + + brokerClient.Connect().Wait() + brokerClient.Subscribe("culmqtt/+/send", 0x01, publishFS20ToCUL) +}