From 67e2ed854eebdb0a73e066728d1ec475f41f61fb Mon Sep 17 00:00:00 2001 From: Jens Christian True Date: Mon, 15 Apr 2019 15:56:24 +0200 Subject: [PATCH] Added Commandline interface --- Dockerfile | 9 ---- Makefile | 2 +- app.go | 101 ------------------------------------------- go.mod | 5 +++ main.go | 36 ++++++++++++++++ run.go | 124 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 166 insertions(+), 111 deletions(-) delete mode 100644 Dockerfile delete mode 100644 app.go create mode 100644 main.go create mode 100644 run.go diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 44a1406..0000000 --- a/Dockerfile +++ /dev/null @@ -1,9 +0,0 @@ -FROM golang:1.11 as builder -WORKDIR /go/gomie -COPY . . -RUN make build - -# Create a minimal container to run a Golang static binary -FROM scratch -COPY --from=builder /go/gomie/gomie . -ENTRYPOINT ["/gomie"] diff --git a/Makefile b/Makefile index 956e014..8537086 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ default: build build: - go build -v + go build dependencies: go mod download diff --git a/app.go b/app.go deleted file mode 100644 index 105833b..0000000 --- a/app.go +++ /dev/null @@ -1,101 +0,0 @@ -package main -import ( - "strconv" - "bytes" - "log" - "net" - "os" - "time" - "github.com/eclipse/paho.mqtt.golang" - "github.com/shirou/gopsutil/host" -) - - -// Get preferred outbound ip of this machine -func GetOutboundIP() net.IP { - conn, err := net.Dial("udp", "8.8.8.8:80") - if err != nil { - log.Fatal(err) - } - defer conn.Close() - - localAddr := conn.LocalAddr().(*net.UDPAddr) - - return localAddr.IP -} - -func getMacAddr() (addr string) { - interfaces, err := net.Interfaces() - if err == nil { - for _, i := range interfaces { - if i.Flags&net.FlagUp != 0 && bytes.Compare(i.HardwareAddr, nil) != 0 { - // Don't use random as we have a real address - addr = i.HardwareAddr.String() - break - } - } - } - return -} - -//Publish a message and wait for confirmation -func SyncPublish(client mqtt.Client, topic string, qos byte, retained bool, payload interface{}) { - - hostname, _ := os.Hostname() - prefix := "homie/" + hostname + "/" - token := client.Publish(prefix + topic, qos, retained, payload) - if(!token.Wait()) { - log.Fatal("Timeout on publish") - log.Fatal(token.Error()) - os.Exit(3) - } -} - -func main() { - opts := mqtt.NewClientOptions().AddBroker("tcp://mqtt.jcktrue.dk:1883").SetClientID("gotrivial") - hostname, _ := os.Hostname() - opts.SetWill("homie/" + hostname + "/$state", "lost", 0, true); - - c := mqtt.NewClient(opts) - if token := c.Connect(); token.Wait() && token.Error() != nil { - panic(token.Error()) - } - - SyncPublish(c, "$state", 0, true, "init") - SyncPublish(c, "$homie", 0, true, "3.0.1") - SyncPublish(c, "$name", 0, true, hostname) - - SyncPublish(c, "$localip", 0, true, GetOutboundIP().String()) - SyncPublish(c, "$mac", 0, true, getMacAddr()) - SyncPublish(c, "$fw/name", 0, true, "gomie") - SyncPublish(c, "$fw/version", 0, true, "1.0") - SyncPublish(c, "$implementation", 0, true, "gomie") - - SyncPublish(c, "$stats", 0, true, "uptime") - uptimeSeconds, _ := host.Uptime() - SyncPublish(c, "$stats/uptime", 0, true, strconv.FormatUint(uptimeSeconds,10)) - SyncPublish(c, "$stats/interval", 0, true, "0") - - SyncPublish(c, "leax-backup/$name", 0, true, "LEAX Backup") - SyncPublish(c, "leax-backup/$type", 0, true, "script") - SyncPublish(c, "leax-backup/$properties", 0, true, "lastrun,output") - - SyncPublish(c, "trueserver-backup/$name", 0, true, "TRUESERVER Backup") - SyncPublish(c, "trueserver-backup/$type", 0, true, "script") - SyncPublish(c, "trueserver-backup/$properties", 0, true, "lastrun,output") - - SyncPublish(c, "$state", 0, true, "ready") - //Done with initialization - - SyncPublish(c, "leax-backup/lastrun", 0, true, strconv.FormatInt(time.Now().Unix(),10)) - SyncPublish(c, "leax-backup/output", 0, true, "Backup successful") - - SyncPublish(c, "trueserver-backup/lastrun", 0, true, strconv.FormatInt(time.Now().Unix(),10)) - SyncPublish(c, "trueserver-backup/output", 0, true, "Backup successful") - - //All done - SyncPublish(c, "$state", 0, true, "disconnected") - - c.Disconnect(1000) //Wait one second to disconnect - } - \ No newline at end of file diff --git a/go.mod b/go.mod index 095d22e..34d20f3 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,12 @@ require ( github.com/StackExchange/wmi v0.0.0-20181212234831-e0a55b97c705 // indirect github.com/eclipse/paho.mqtt.golang v1.1.1 github.com/go-ole/go-ole v1.2.4 // indirect + github.com/google/uuid v1.1.1 + github.com/inconshreveable/mousetrap v1.0.0 // indirect github.com/shirou/gopsutil v2.18.12+incompatible github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4 // indirect + github.com/spf13/cobra v0.0.3 + github.com/spf13/pflag v1.0.3 // indirect + github.com/urfave/cli v1.20.0 golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 // indirect ) diff --git a/main.go b/main.go new file mode 100644 index 0000000..4c1b8d6 --- /dev/null +++ b/main.go @@ -0,0 +1,36 @@ +package main + +import ( + "log" + "os" + + "github.com/urfave/cli" +) + +func main() { + app := cli.NewApp() + app.Name = "gomie" + app.Usage = "Simulating a Homie IOT device through Go" + app.Version = "0.0.1" + + app.Flags = []cli.Flag{ + cli.StringFlag{ + Name: "mqtt, m", + Usage: "mqtt connection string", + Value: "tcp://localhost:1883", + }, + } + + app.Commands = []cli.Command{ + { + Name: "run", + Usage: "Run a script and report to the Homie server", + Action: mqttstuff, + }, + } + + err := app.Run(os.Args) + if err != nil { + log.Fatal(err) + } +} diff --git a/run.go b/run.go new file mode 100644 index 0000000..12ce5a8 --- /dev/null +++ b/run.go @@ -0,0 +1,124 @@ +package main +import ( + "strconv" + "bytes" + "log" + "net" + "os" + "time" + "os/exec" + "github.com/eclipse/paho.mqtt.golang" + "github.com/shirou/gopsutil/host" + "github.com/google/uuid" + "github.com/urfave/cli" +) + + +// Get preferred outbound ip of this machine +func GetOutboundIP() net.IP { + conn, err := net.Dial("udp", "8.8.8.8:80") + if err != nil { + log.Fatal(err) + } + defer conn.Close() + + localAddr := conn.LocalAddr().(*net.UDPAddr) + + return localAddr.IP +} + +//Get the Mac address +func getMacAddr() (addr string) { + interfaces, err := net.Interfaces() + if err == nil { + for _, i := range interfaces { + if i.Flags&net.FlagUp != 0 && bytes.Compare(i.HardwareAddr, nil) != 0 { + // Don't use random as we have a real address + addr = i.HardwareAddr.String() + break + } + } + } + return +} + +//Publish a message and wait for confirmation +func SyncPublish(client mqtt.Client, topic string, qos byte, retained bool, payload interface{}) { + + hostname, _ := os.Hostname() + prefix := "homie/" + hostname + "/" + token := client.Publish(prefix + topic, qos, retained, payload) + if(!token.Wait()) { + log.Fatal("Timeout on publish") + os.Exit(1) + } +} + +func mqttstuff(cli *cli.Context) { + clientid := uuid.New() + + command := ""; + if cli.NArg() > 0 { + command = cli.Args()[0] + } else { + log.Fatal("You must provide a command to run") + os.Exit(1) + } + + log.Printf("Connection string: %s\n",cli.GlobalString("mqtt")) + + opts := mqtt.NewClientOptions().AddBroker(cli.GlobalString("mqtt")).SetClientID(clientid.String()) + hostname, _ := os.Hostname() + opts.SetWill("homie/" + hostname + "/$state", "lost", 0, true); + + client := mqtt.NewClient(opts) + log.Println("Connecting...") + if token := client.Connect(); token.Wait() && token.Error() != nil { + log.Fatal(token.Error()) + os.Exit(1) + } + log.Println("Connected") + + SyncPublish(client, "$state", 0, true, "init") + log.Println("$state -> init") + SyncPublish(client, "$homie", 0, true, "3.0.1") + SyncPublish(client, "$name", 0, true, hostname) + + SyncPublish(client, "$localip", 0, true, GetOutboundIP().String()) + SyncPublish(client, "$mac", 0, true, getMacAddr()) + SyncPublish(client, "$fw/name", 0, true, "gomie") + SyncPublish(client, "$fw/version", 0, true, "1.0") + SyncPublish(client, "$implementation", 0, true, "gomie") + + SyncPublish(client, "$stats", 0, true, "uptime") + uptimeSeconds, _ := host.Uptime() + SyncPublish(client, "$stats/uptime", 0, true, strconv.FormatUint(uptimeSeconds,10)) + SyncPublish(client, "$stats/interval", 0, true, "0") + + SyncPublish(client, "leax-backup/$name", 0, true, "LEAX Backup") + SyncPublish(client, "leax-backup/$type", 0, true, "script") + SyncPublish(client, "leax-backup/$properties", 0, true, "lastrun,stdout,errorcode") + + SyncPublish(client, "$state", 0, true, "ready") + log.Println("$state -> ready") + //Done with initialization + + SyncPublish(client, "leax-backup/started", 0, true, strconv.FormatInt(time.Now().Unix(),10)) + SyncPublish(client, "leax-backup/stdout", 0, true, "") + out, err := exec.Command(command).Output() + if err != nil { + log.Fatal(err) + } + + SyncPublish(client, "leax-backup/ended", 0, true, strconv.FormatInt(time.Now().Unix(),10)) + SyncPublish(client, "leax-backup/stdout", 0, true, out) + + + //All done + SyncPublish(client, "$state", 0, true, "disconnected") + log.Println("$state -> disconnected") + + client.Disconnect(1000) //Wait one second to disconnect + log.Println("Disconnected") + } + \ No newline at end of file