Re-introduce meteod

meteod is a standalone gateway application and http server for pushing
values to an InfluxDB database and acting as a REST API webserver for
querying of the current measurements.
This commit is contained in:
Felix Niederwanger 2022-12-23 14:03:00 +01:00
parent 69211935ba
commit cc649462ea
Signed by: phoenix
GPG key ID: 31860289A704FB3C
11 changed files with 556 additions and 350 deletions

4
.gitignore vendored
View file

@ -34,6 +34,7 @@
# Debug files
*.dSYM/
__debug_bin
# GoLand
.idea
@ -44,8 +45,9 @@
# Build files
build
build/
/meteo-influx-gateway
/meteod
## Custom config files
*.conf
*.toml
*.ini

View file

@ -1,11 +1,11 @@
default: all
all: meteo-influx-gateway
all: meteod
install: all
## === Builds =============================================================== ##
meteo-influx-gateway: cmd/meteo-influx-gateway/meteo-mqtt-influxdb.go cmd/meteo-influx-gateway/influxdb.go cmd/meteo-influx-gateway/mqtt.go
meteod: cmd/meteod/*.go
go build -o $@ $^
## === Tests ================================================================ ##

View file

@ -1,55 +0,0 @@
package main
import (
"net/url"
"time"
client "github.com/influxdata/influxdb1-client"
)
type InfluxDB struct {
database string
client *client.Client
}
func ConnectInfluxDB(remote string, username string, password string, database string) (InfluxDB, error) {
var influx InfluxDB
influx.database = database
host, err := url.Parse(remote)
if err != nil {
return influx, err
}
conf := client.Config{
URL: *host,
Username: username,
Password: password,
}
influx.client, err = client.NewClient(conf)
return influx, err
}
// Ping the InfluxDB server
func (influx *InfluxDB) Ping() (time.Duration, string, error) {
return influx.client.Ping()
}
// Write a measurement into the database
func (influx *InfluxDB) Write(measurement string, tags map[string]string, fields map[string]interface{}) error {
point := client.Point{
Measurement: measurement,
Tags: tags,
Fields: fields,
//Time: time.Now(),
Precision: "s",
}
pts := make([]client.Point, 0)
pts = append(pts, point)
bps := client.BatchPoints{
Points: pts,
Database: influx.database,
//RetentionPolicy: "default",
}
_, err := influx.client.Write(bps)
return err
}

View file

@ -1,279 +0,0 @@
package main
import (
"encoding/json"
"fmt"
"os"
"os/signal"
"strconv"
"strings"
"syscall"
mqtt "github.com/eclipse/paho.mqtt.golang"
"gopkg.in/ini.v1"
)
// Config is the internal singleton configuration for this program
type Config struct {
MqttHost string `ini:"mqtt,remote"`
MqttClientID string `ini:"mqtt,clientid"`
InfluxHost string `ini:"influxdb,remote"`
InfluxUsername string `ini:"influxdb,username"`
InfluxPassword string `ini:"influxdb,password"`
InfluxDatabase string `ini:"influxdb,database"`
Verbose bool
}
func (c *Config) loadIni(filename string) error {
cfg, err := ini.Load(filename)
if err != nil {
return err
}
mqtt := cfg.Section("mqtt")
mqtthost := mqtt.Key("remote").String()
if mqtthost != "" {
c.MqttHost = mqtthost
}
mqttclientid := mqtt.Key("clientid").String()
if mqttclientid != "" {
c.MqttClientID = mqttclientid
}
influx := cfg.Section("influxdb")
influxhost := influx.Key("remote").String()
if influxhost != "" {
c.InfluxHost = influxhost
}
influxuser := influx.Key("username").String()
if influxuser != "" {
c.InfluxUsername = influxuser
}
influxpass := influx.Key("password").String()
if influxpass != "" {
c.InfluxPassword = influxpass
}
influxdb := influx.Key("database").String()
if influxdb != "" {
c.InfluxDatabase = influxdb
}
return nil
}
var config Config
var influx InfluxDB
func assembleJson(node int, data map[string]interface{}) string {
ret := fmt.Sprintf("node %d: {", node)
first := true
for k, v := range data {
if first {
first = false
} else {
ret += ", "
}
ret += fmt.Sprintf("\"%s\":%f", k, v)
}
ret += "}"
return ret
}
func received(msg mqtt.Message) {
data := make(map[string]interface{}, 0)
if err := json.Unmarshal(msg.Payload(), &data); err != nil {
fmt.Fprintf(os.Stderr, "json unmarshall error: %s\n", err)
return
}
// We don't log the name, remove it, if present
if _, ok := data["name"]; ok {
delete(data, "name")
}
// ID is taken from the topic
if _, ok := data["id"]; ok {
delete(data, "id")
}
// Parse node ID from topic
nodeID, err := strconv.Atoi(msg.Topic()[6:])
if err != nil {
fmt.Fprintf(os.Stderr, "invalid meteo id\n")
return
}
// Write to InfluxDB
for k, v := range data {
tags := map[string]string{"node": fmt.Sprintf("%d", nodeID)}
f, err := strconv.ParseFloat(fmt.Sprintf("%f", v), 32)
if err != nil {
fmt.Fprintf(os.Stderr, "non-float value received: %s\n", err)
return
}
fields := map[string]interface{}{"value": f}
if err := influx.Write(k, tags, fields); err != nil {
fmt.Fprintf(os.Stderr, "cannot write to influxdb: %s\n", err)
return
}
}
// OK
if config.Verbose {
fmt.Println(assembleJson(nodeID, data))
}
}
// awaits SIGINT or SIGTERM
func awaitTerminationSignal() {
sigs := make(chan os.Signal, 1)
done := make(chan bool, 1)
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
go func() {
sig := <-sigs
fmt.Println(sig)
done <- true
}()
<-done
}
func printUsage() {
fmt.Println("meteo-influxdb-gateway")
fmt.Printf("Usage: %s [OPTIONS]\n", os.Args[0])
fmt.Println("OPTIONS")
fmt.Println(" -h,--help Display this help message")
fmt.Println(" -c,--config FILE Load config file")
fmt.Println(" -v, --verbose Verbose mode")
fmt.Println(" --mqtt MQTT Set mqtt server")
fmt.Println(" --mqtt-client-id ID Set mqtt client id")
fmt.Println(" --influx HOST Set influxdb hostname")
fmt.Println(" --username USER Set influxdb username")
fmt.Println(" --password PASS Set influxdb password")
fmt.Println(" --database DB Set influxdb database")
}
func fileExists(filename string) bool {
if _, err := os.Stat(filename); os.IsNotExist(err) {
return false
}
return true
}
func main() {
var err error
// Default settings
config.MqttHost = "127.0.0.1"
config.InfluxHost = "http://127.0.0.1:8086"
config.InfluxUsername = "meteo"
config.InfluxPassword = ""
config.InfluxDatabase = "meteo"
config.Verbose = false
configFile := "/etc/meteo/meteo-influx-gateway.ini"
if fileExists(configFile) {
if err := config.loadIni(configFile); err != nil {
fmt.Fprintf(os.Stderr, "error loading ini file %s: %s\n", configFile, err)
os.Exit(1)
}
}
args := os.Args[1:]
for i := 0; i < len(args); i++ {
arg := strings.TrimSpace(args[i])
if arg == "" {
continue
} else if arg[0] == '-' {
last := i >= len(args)-1
if arg == "-h" || arg == "--help" {
printUsage()
return
} else if arg == "-c" || arg == "--config" {
if last {
fmt.Fprintf(os.Stderr, "Missing argument: config file\n")
os.Exit(1)
}
i++
configFile = args[i]
if err := config.loadIni(configFile); err != nil {
fmt.Fprintf(os.Stderr, "error loading ini file %s: %s\n", configFile, err)
os.Exit(1)
}
} else if arg == "-v" || arg == "--verbose" {
config.Verbose = true
} else if arg == "--mqtt" {
if last {
fmt.Fprintf(os.Stderr, "Missing argument: mqtt remote\n")
os.Exit(1)
}
i++
config.MqttHost = args[i]
} else if arg == "--mqtt-client-id" {
if last {
fmt.Fprintf(os.Stderr, "Missing argument: mqtt-client-id remote\n")
os.Exit(1)
}
i++
config.MqttClientID = args[i]
} else if arg == "--influx" {
if last {
fmt.Fprintf(os.Stderr, "Missing argument: influx remote\n")
os.Exit(1)
}
i++
config.InfluxHost = args[i]
} else if arg == "--username" {
if last {
fmt.Fprintf(os.Stderr, "Missing argument: influx username\n")
os.Exit(1)
}
i++
config.InfluxUsername = args[i]
} else if arg == "--password" {
if last {
fmt.Fprintf(os.Stderr, "Missing argument: influx password\n")
os.Exit(1)
}
i++
config.InfluxPassword = args[i]
} else if arg == "--database" {
if last {
fmt.Fprintf(os.Stderr, "Missing argument: influx database\n")
os.Exit(1)
}
i++
config.InfluxDatabase = args[i]
}
} else {
fmt.Fprintf(os.Stderr, "Invalid argument: %s\n", arg)
os.Exit(1)
}
}
// Connect InfluxDB
influx, err = ConnectInfluxDB(config.InfluxHost, config.InfluxUsername, config.InfluxPassword, config.InfluxDatabase)
if err != nil {
fmt.Fprintf(os.Stderr, "cannot connect to influxdb: %s\n", err)
os.Exit(1)
}
if ping, version, err := influx.Ping(); err != nil {
fmt.Fprintf(os.Stderr, "cannot ping influxdb: %s\n", err)
os.Exit(1)
} else {
if config.Verbose {
fmt.Printf("influxdb connected: v%s (Ping: %d ms)\n", version, ping.Milliseconds())
}
}
// Connect to mqtt server
mqtt := MQTT{}
mqtt.ClientID = config.MqttClientID
mqtt.Verbose = config.Verbose
mqtt.Callback = received
if err := mqtt.Connect(config.MqttHost, 1883); err != nil {
fmt.Fprintf(os.Stderr, "mqtt error: %s\n", err)
os.Exit(1)
}
fmt.Println("meteo-mqtt-influx gateway is up and running")
awaitTerminationSignal()
}

48
cmd/meteod/influxdb.go Normal file
View file

@ -0,0 +1,48 @@
package main
import (
"context"
"fmt"
"time"
influxdb2 "github.com/influxdata/influxdb-client-go/v2"
)
type InfluxDB struct {
client influxdb2.Client
}
func ConnectInfluxDB(remote string, token string) (InfluxDB, error) {
var influx InfluxDB
influx.client = influxdb2.NewClient(remote, token)
return influx, nil
}
func (influx *InfluxDB) Ping(timeout time.Duration) (time.Duration, error) {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
now := time.Now()
select {
case <-ctx.Done():
// timeout
return time.Duration(0), fmt.Errorf("timeout")
default:
succ, err := influx.client.Ping(ctx)
delta := time.Now().Sub(now)
if err != nil {
return delta, err
}
if !succ {
return delta, fmt.Errorf("no reply from server")
}
return delta, nil
}
}
// Write a measurement into the database
func (influx *InfluxDB) Write(org, bucket, measurement string, tags map[string]string, fields map[string]interface{}) error {
writeAPI := influx.client.WriteAPIBlocking(org, bucket)
p := influxdb2.NewPoint(measurement, tags, fields, time.Now())
err := writeAPI.WritePoint(context.Background(), p)
return err
}

405
cmd/meteod/meteod.go Normal file
View file

@ -0,0 +1,405 @@
package main
import (
"encoding/json"
"fmt"
"log"
"net/http"
"os"
"os/signal"
"strconv"
"strings"
"syscall"
"time"
mqtt "github.com/eclipse/paho.mqtt.golang"
"gopkg.in/ini.v1"
)
// Config is the internal singleton configuration for this program
type Config struct {
MqttHost string
MqttClientID string
InfluxHost string
InfluxBucket string
InfluxOrg string
InfluxToken string
HttpBindAddr string
Verbose bool
}
type Measurement struct {
Node int `json:"id"` // NodeID
NodeName string `json:"name"` // Name of the measurement node
Timestamp int64 `json:"tsp"` // Timestamp when the measurement was taken
Measurement string `json:"measurement"` // Measurement name
Values map[string]float32 `json:"values"` // Measurement values
}
func (mst *Measurement) json() string {
ret := fmt.Sprintf("node %d: {", mst.Node)
first := true
for k, v := range mst.Values {
if first {
first = false
} else {
ret += ", "
}
ret += fmt.Sprintf("\"%s\":%f", k, v)
}
ret += "}"
return ret
}
func (c *Config) loadIni(filename string) error {
cfg, err := ini.Load(filename)
if err != nil {
return err
}
mqtt := cfg.Section("mqtt")
if mqtthost := mqtt.Key("remote").String(); mqtthost != "" {
c.MqttHost = mqtthost
}
if mqttclientid := mqtt.Key("clientid").String(); mqttclientid != "" {
c.MqttClientID = mqttclientid
}
influx := cfg.Section("influxdb")
if influxhost := influx.Key("remote").String(); influxhost != "" {
c.InfluxHost = influxhost
}
if influxOrg := influx.Key("organization").String(); influxOrg != "" {
c.InfluxOrg = influxOrg
}
if influxToken := influx.Key("token").String(); influxToken != "" {
c.InfluxToken = influxToken
}
if influxdb := influx.Key("bucket").String(); influxdb != "" {
c.InfluxBucket = influxdb
}
http := cfg.Section("http")
if bindAddr := http.Key("bind_addr").String(); bindAddr != "" {
c.HttpBindAddr = bindAddr
}
return nil
}
var config Config
var influx InfluxDB
var nodes map[int]Measurement
func receiveMeasurement(msg mqtt.Message) (Measurement, error) {
var mst Measurement
var err error
payload := msg.Payload()
mst.Timestamp = time.Now().Unix()
mst.Measurement = "meteo"
// Try to parse the new data field version
// The measurement is considered as valid, if there is values defined
// otherwise we continue with the old data parsing
if err := json.Unmarshal(payload, &mst); err == nil && mst.Values != nil {
return mst, nil
}
// Proceed with the old data parsing
data := make(map[string]interface{}, 0)
if err := json.Unmarshal(msg.Payload(), &data); err != nil {
return mst, err
}
// Parse node ID from topic
mst.Node, err = strconv.Atoi(msg.Topic()[6:])
if err != nil {
return mst, fmt.Errorf("invalid meteo id")
}
// remove unnecessary fields
delete(data, "id")
delete(data, "name")
delete(data, "measurement")
// Parse fields
mst.Values = make(map[string]float32)
for k, v := range data {
f, err := strconv.ParseFloat(fmt.Sprintf("%f", v), 32)
if err != nil {
return mst, fmt.Errorf("invalid measurement value for %s", k)
}
mst.Values[k] = float32(f)
}
return mst, nil
}
func received(msg mqtt.Message) {
mst, err := receiveMeasurement(msg)
if err != nil {
fmt.Fprintf(os.Stderr, "error receiving measurement: %s\n", err)
return
}
nodes[mst.Node] = mst
tags := map[string]string{"node": fmt.Sprintf("%d", mst.Node)}
// Write measurement as a whole
// Write each value as own measurement
fields := make(map[string]interface{}, 0)
for k, v := range mst.Values {
fields[k] = v
}
if err := influx.Write(config.InfluxOrg, config.InfluxBucket, mst.Measurement, tags, fields); err != nil {
fmt.Fprintf(os.Stderr, "cannot write to influxdb: %s\n", err)
return
}
if config.Verbose {
fmt.Println(mst.json())
}
}
// awaits SIGINT or SIGTERM
func awaitTerminationSignal() {
sigs := make(chan os.Signal, 1)
done := make(chan bool, 1)
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
go func() {
sig := <-sigs
fmt.Println(sig)
done <- true
}()
<-done
}
func printUsage() {
fmt.Println("meteod")
fmt.Printf("Usage: %s [OPTIONS]\n", os.Args[0])
fmt.Println("OPTIONS")
fmt.Println(" -h,--help Display this help message")
fmt.Println(" -c,--config FILE Load config file")
fmt.Println(" -v, --verbose Verbose mode")
fmt.Println(" --mqtt MQTT Set mqtt server")
fmt.Println(" --mqtt-client-id ID Set mqtt client id")
fmt.Println(" --influx HOST Set influxdb hostname")
fmt.Println(" --org ORGANIZATION Set influxdb organization")
fmt.Println(" --bucket BUCKET Set influxdb bucket")
fmt.Println(" --token TOKEN Set influxdb token")
fmt.Println(" --http BINDADDR Enable http server on the given address")
}
func fileExists(filename string) bool {
if _, err := os.Stat(filename); os.IsNotExist(err) {
return false
}
return true
}
func main() {
var err error
nodes = make(map[int]Measurement)
// Default settings
config.MqttHost = "127.0.0.1"
config.InfluxHost = "http://127.0.0.1:8086"
config.InfluxBucket = "meteo"
config.InfluxToken = ""
config.Verbose = false
configFile := "/etc/meteo/meteod.conf"
if fileExists(configFile) {
if err := config.loadIni(configFile); err != nil {
fmt.Fprintf(os.Stderr, "error loading config file %s: %s\n", configFile, err)
os.Exit(1)
}
}
args := os.Args[1:]
for i := 0; i < len(args); i++ {
arg := strings.TrimSpace(args[i])
if arg == "" {
continue
} else if arg[0] == '-' {
last := i >= len(args)-1
if arg == "-h" || arg == "--help" {
printUsage()
return
} else if arg == "-c" || arg == "--config" {
if last {
fmt.Fprintf(os.Stderr, "Missing argument: config file\n")
os.Exit(1)
}
i++
configFile = args[i]
if err := config.loadIni(configFile); err != nil {
fmt.Fprintf(os.Stderr, "error loading ini file %s: %s\n", configFile, err)
os.Exit(1)
}
} else if arg == "-v" || arg == "--verbose" {
config.Verbose = true
} else if arg == "--mqtt" {
if last {
fmt.Fprintf(os.Stderr, "Missing argument: mqtt remote\n")
os.Exit(1)
}
i++
config.MqttHost = args[i]
} else if arg == "--mqtt-client-id" {
if last {
fmt.Fprintf(os.Stderr, "Missing argument: mqtt-client-id remote\n")
os.Exit(1)
}
i++
config.MqttClientID = args[i]
} else if arg == "--influx" {
if last {
fmt.Fprintf(os.Stderr, "Missing argument: influx remote\n")
os.Exit(1)
}
i++
config.InfluxHost = args[i]
} else if arg == "--token" {
if last {
fmt.Fprintf(os.Stderr, "Missing argument: influx token\n")
os.Exit(1)
}
i++
config.InfluxToken = args[i]
} else if arg == "--bucket" || arg == "--database" {
if last {
fmt.Fprintf(os.Stderr, "Missing argument: influx bucket\n")
os.Exit(1)
}
i++
config.InfluxBucket = args[i]
} else if arg == "--org" || arg == "--organization" {
if last {
fmt.Fprintf(os.Stderr, "Missing argument: influx organization\n")
os.Exit(1)
}
i++
config.InfluxOrg = args[i]
} else if arg == "--http" {
if last {
fmt.Fprintf(os.Stderr, "Missing argument: http bind address\n")
os.Exit(1)
}
i++
config.HttpBindAddr = args[i]
} else {
fmt.Fprintf(os.Stderr, "Invalid argument: %s\n", arg)
os.Exit(1)
}
} else {
fmt.Fprintf(os.Stderr, "Invalid argument: %s\n", arg)
os.Exit(1)
}
}
// Connect InfluxDB
influx, err = ConnectInfluxDB(config.InfluxHost, config.InfluxToken)
if err != nil {
fmt.Fprintf(os.Stderr, "cannot connect to influxdb: %s\n", err)
os.Exit(1)
}
if ping, err := influx.Ping(5 * time.Second); err != nil {
fmt.Fprintf(os.Stderr, "cannot ping influxdb: %s\n", err)
os.Exit(1)
} else {
if config.Verbose {
fmt.Printf("influxdb connected (Ping: %d ms)\n", ping.Milliseconds())
}
}
// Connect to mqtt server
mqtt := MQTT{}
mqtt.ClientID = config.MqttClientID
mqtt.Verbose = config.Verbose
mqtt.Callback = received
if err := mqtt.Connect(config.MqttHost, 1883); err != nil {
fmt.Fprintf(os.Stderr, "mqtt error: %s\n", err)
os.Exit(1)
}
// Enable http server
if config.HttpBindAddr != "" {
http.HandleFunc("/", handlerDefault)
http.HandleFunc("/health", handlerHealth)
http.HandleFunc("/health.json", handlerHealth)
/* API Version 1 */
http.HandleFunc("/api", handlerAPIDefault)
http.HandleFunc("/api/v1/health", handlerHealth)
http.HandleFunc("/api/v1/nodes", handlerNodes)
http.HandleFunc("/api/v1/measurements", handlerMeasurements)
// Listen on http server
go func() {
log.Fatal(http.ListenAndServe(config.HttpBindAddr, nil))
}()
if config.Verbose {
fmt.Printf("webserver enabled on http://%s\n", config.HttpBindAddr)
}
}
fmt.Println("meteod is running")
awaitTerminationSignal()
}
func replyJson(w http.ResponseWriter, obj interface{}) {
buf, err := json.Marshal(obj)
if err != nil {
fmt.Fprintf(os.Stderr, "json marshall error: %s\n", err)
w.WriteHeader(500)
w.Write([]byte("server error"))
return
}
w.Header().Add("Content-Type", "application/json")
w.Header().Add("Content-Length", fmt.Sprintf("%d", len(buf)))
w.WriteHeader(200)
w.Write(buf)
}
func handlerDefault(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("meteod"))
}
func handlerAPIDefault(w http.ResponseWriter, r *http.Request) {
content := []byte("<!DOCTYPE html>\n<html xmlns=\"http://www.w3.org/1999/xhtml\">\n<head><title>meteod api</title></head>\n<body>\n<h3>meteod api</h3>\n<p>Available routes</p>\n<ul>\n<li><a href=\"api/v1/health\">/api/v1/health</a></li>\n<li><a href=\"api/v1/nodes\">/api/v1/nodes</a></li>\n<li><a href=\"api/v1/measurements\">/api/v1/measurements</a></li></ul>\n</body>\n</html>")
w.Header().Add("Content-Type", "text/html")
w.Header().Add("Content-Length", fmt.Sprintf("%d", len(content)))
w.Write(content)
}
func handlerMeasurements(w http.ResponseWriter, r *http.Request) {
// Post array of current measurements
mst := make([]Measurement, 0)
for _, m := range nodes {
mst = append(mst, m)
}
replyJson(w, mst)
}
func handlerNodes(w http.ResponseWriter, r *http.Request) {
// Post array of current nodes
type Node struct {
ID int `json:"id"`
Name string `json:"name"`
}
ret := make([]Node, 0)
for _, m := range nodes {
ret = append(ret, Node{ID: m.Node, Name: m.NodeName})
}
replyJson(w, ret)
}
func handlerHealth(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "{\"status\":\"ok\"}")
}

2
go.mod
View file

@ -4,7 +4,7 @@ go 1.14
require (
github.com/eclipse/paho.mqtt.golang v1.3.5
github.com/influxdata/influxdb1-client v0.0.0-20200827194710-b269163b24ab
github.com/influxdata/influxdb-client-go/v2 v2.12.1
github.com/smartystreets/goconvey v1.6.4 // indirect
gopkg.in/ini.v1 v1.62.0
)

88
go.sum
View file

@ -1,24 +1,106 @@
github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/deepmap/oapi-codegen v1.8.2 h1:SegyeYGcdi0jLLrpbCMoJxnUUn8GBXHsvr4rbzjuhfU=
github.com/deepmap/oapi-codegen v1.8.2/go.mod h1:YLgSKSDv/bZQB7N4ws6luhozi3cEdRktEqrX88CvjIw=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/eclipse/paho.mqtt.golang v1.3.5 h1:sWtmgNxYM9P2sP+xEItMozsR3w0cqZFlqnNN1bdl41Y=
github.com/eclipse/paho.mqtt.golang v1.3.5/go.mod h1:eTzb4gxwwyWpqBUHGQZ4ABAV7+Jgm1PklsYT/eo8Hcc=
github.com/getkin/kin-openapi v0.61.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-chi/chi/v5 v5.0.0/go.mod h1:BBug9lr0cqtdAhsu6R4AAdvufI0/XBzAQSsUqJpoZOs=
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
github.com/golangci/lint-1 v0.0.0-20181222135242-d2cdd8c08219/go.mod h1:/X8TswGSh1pIozq4ZwCfxS0WA5JGXguxk94ar/4c87Y=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/influxdata/influxdb1-client v0.0.0-20200827194710-b269163b24ab h1:HqW4xhhynfjrtEiiSGcQUd6vrK23iMam1FO8rI7mwig=
github.com/influxdata/influxdb1-client v0.0.0-20200827194710-b269163b24ab/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
github.com/influxdata/influxdb-client-go/v2 v2.12.1 h1:RrjoDNyBGFYvjKfjmtIyYAn6GY/SrtocSo4RPlt+Lng=
github.com/influxdata/influxdb-client-go/v2 v2.12.1/go.mod h1:YteV91FiQxRdccyJ2cHvj2f/5sq4y4Njqu1fQzsQCOU=
github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839 h1:W9WBk7wlPfJLvMCdtV4zPulc4uCPrlywQOmbFOhgQNU=
github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839/go.mod h1:xaLFMmpvUxqXtVkUJfg9QmT88cDaCJ3ZKgdZ78oO8Qo=
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/labstack/echo/v4 v4.2.1/go.mod h1:AA49e0DZ8kk5jTOOCKNuPR6oTnBS0dYiM4FW1e6jwpg=
github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k=
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/matryer/moq v0.0.0-20190312154309-6cfb0558e1bd/go.mod h1:9ELz6aaclSIGnZBoaSLZ3NAl1VTufbOrXBPvtcy6WiQ=
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20200425230154-ff2c4b7c35a0 h1:Jcxah/M+oLZ/R4/z5RzfPzGbPXnVDPkEDtf2JnuxN+U=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200425230154-ff2c4b7c35a0/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20210119194325-5f4716e94777 h1:003p0dJM77cxMSyCPFphvZf/Y5/NXf5fzg6ufd1/Oew=
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/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-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU=
gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View file

@ -1,9 +0,0 @@
[mqtt]
remote = "127.0.0.1"
clientid = "meteo-influx-gateway"
[influxdb]
remote = "http://127.0.0.1:8086"
username = "meteo"
password = "meteo"
database = "meteo"

12
meteod.conf.example Normal file
View file

@ -0,0 +1,12 @@
[mqtt]
remote = "127.0.0.1"
clientid = "meteod"
[influxdb]
remote = "http://127.0.0.1:8086"
organization = "meteo"
bucket = "meteo"
token = ""
[http]
bind_addr = "127.0.0.1:4042"