From 85ea0e2b06d419ebe585d72f9159bc5be4465738 Mon Sep 17 00:00:00 2001 From: Joerg Lehmann Date: Fri, 12 Jul 2019 19:11:34 +0200 Subject: [PATCH] first try of implementing sms alerts --- alert.go | 41 ++++++++++++++ lorahandler.go | 26 ++++++++- persistence.go | 143 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 208 insertions(+), 2 deletions(-) create mode 100644 alert.go create mode 100644 persistence.go diff --git a/alert.go b/alert.go new file mode 100644 index 0000000..8109738 --- /dev/null +++ b/alert.go @@ -0,0 +1,41 @@ +package main + +import ( + "fmt" + "io/ioutil" + "log" + "time" + "net/http" + "net/url" +) + +func sendSMS(phonenumber string, alertMessage string) { + myurl := fmt.Sprintf("https://api.smsapi.com/sms.do?to=%s&message=%s&format=json",phonenumber,url.QueryEscape(alertMessage)) + + req, err := http.NewRequest("GET", myurl, nil) + if err != nil { + log.Fatal("Error reading request. ", err) + } + + req.Header.Set("Authorization", "Bearer IQ4vRG2JvNOmYmrYz6RuSwAanYZgd2hHGwtN62kq") + + client := &http.Client{Timeout: time.Second * 10} + + resp, err := client.Do(req) + if err != nil { + log.Fatal("Error reading response. ", err) + } + defer resp.Body.Close() + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + log.Fatal("Error reading body. ", err) + } + + fmt.Printf("%s\n", body) +} + +func sendAlert(deveui string, alertMessage string) { + fmt.Printf("sendAlert: deveui=%s, message=%s\n", deveui, alertMessage) + sendSMS("41765006123",alertMessage) +} diff --git a/lorahandler.go b/lorahandler.go index d5527a1..7b59838 100644 --- a/lorahandler.go +++ b/lorahandler.go @@ -86,6 +86,7 @@ type payload_1 struct { // Global variables var file *os.File +var alertMap map[string]string func DecodePayload(s string, deveui string, devaddr string, lrrlat float32, lrrlon float32, write2file bool) { var ba []byte @@ -123,7 +124,7 @@ func DecodePayload(s string, deveui string, devaddr string, lrrlat float32, lrrl // Time of first Packet var tfp = (time.Now().Unix() / 60) - int64(pl_1.O) var step = int64(pl_1.O / 7) - + t := pl_1.T WriteDatapoint(tfp,deveui,devaddr,pl_1.Vbat,pl_1.H1,pl_1.P1,pl_1.W1,0,0,t,lrrlat,lrrlon) t = t + int16(pl_1.TC1) @@ -159,7 +160,11 @@ func DecodePayload(s string, deveui string, devaddr string, lrrlat float32, lrrl WriteDatapoint(tfp,deveui,devaddr,pl_128.Vbat,pl_128.H,pl_128.P,pl_128.W,pl_128.W1,pl_128.W2,pl_128.T,lrrlat,lrrlon) } } - + // Send alert if necessary + if val, ok := alertMap[deveui]; ok { + sendAlert(deveui,val) + delete(alertMap,deveui) + } } func WriteDatapoint(mytime int64, deveui string, devaddr string, v uint8, h uint8, p uint8, w uint16, w1 int32, w2 int32, t int16, lrrlat float32, lrrlon float32) { @@ -182,6 +187,18 @@ func WriteDatapoint(mytime int64, deveui string, devaddr string, v uint8, h uint } WriteStringToFile(s) + + w_gram := w*5 + addValue(deveui,w_gram) + w_loss := getMaxValue(deveui) - w_gram; + if (w_loss > 500) { + // Schwarmalarm! + s = fmt.Sprintf("alert,deveui=%s reason=\"swarmalarm\",w=%di,w_loss=%di %d\n",deveui,w_gram,w_loss,mytime*60*1000*1000*1000) + location, _ := time.LoadLocation("Europe/Zurich") + alertMap[deveui] = fmt.Sprintf("*** Schwarmalarm ***\n%s\n%s\nGewichtsverlust: %d g",getDevAlias(deveui),time.Unix(mytime*60,0).In(location).Format("02.01.2006 15:04"),w_loss) + WriteStringToFile(s) + + } } } @@ -198,6 +215,11 @@ func WriteStringToFile(s string) { } func main() { + // Init Redis + initDB() + + // Init alertMap + alertMap = make(map[string]string) // Open Output File f, err := os.OpenFile(outputfile,os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644) diff --git a/persistence.go b/persistence.go new file mode 100644 index 0000000..3d0df42 --- /dev/null +++ b/persistence.go @@ -0,0 +1,143 @@ +package main + +import ( + "fmt" + "time" + "github.com/gomodule/redigo/redis" +) + +var globalPool *redis.Pool + +const lastvaluesPrefix string = "lastvalues:" +const devPrefix string = "dev:" + +func newPool() *redis.Pool { + return &redis.Pool{ + // Maximum number of idle connections in the pool. + MaxIdle: 80, + // max number of connections + MaxActive: 12000, + // Dial is an application supplied function for creating and + // configuring a connection. + Dial: func() (redis.Conn, error) { + c, err := redis.Dial("tcp", ":6379") + if err != nil { + panic(err.Error()) + } + return c, err + }, + } +} + +// ping tests connectivity for redis (PONG should be returned) +func ping(c redis.Conn) error { + // Send PING command to Redis + // PING command returns a Redis "Simple String" + // Use redis.String to convert the interface type to string + _, err := redis.String(c.Do("PING")) + if err != nil { + return err + } + + return nil +} + +func initDB() { + + // newPool returns a pointer to a redis.Pool + pool := newPool() + // get a connection from the globalPool (redis.Conn) + conn := pool.Get() + defer conn.Close() + + globalPool = pool + + // wir machen einen Connection Test + ping(conn) + + addValue("0000000000000000",uint16(time.Now().Unix())); +} + +func closeDB() { + globalPool.Close() +} + +func checkDevAvailable(deveui string) bool { + conn := globalPool.Get() + defer conn.Close() + + _, err := redis.String(conn.Do("GET", lastvaluesPrefix + deveui)) + if (err == redis.ErrNil) { + return true + } else if err != nil { + return false + } + return false +} + +func getMaxValue(deveui string) uint16 { + var res uint16 = 0 + var myvalues []uint16 + conn := globalPool.Get() + defer conn.Close() + + values, _ := redis.Values(conn.Do("LRANGE", lastvaluesPrefix + deveui, 0, -1)) + + if err := redis.ScanSlice(values, &myvalues); err != nil { + fmt.Println(err) + return 0 + } + + for _, value := range myvalues { + if (uint16(value) > res) { + res = uint16(value) + } + } + + return res +} + +func addValue(deveui string, value uint16) { + conn := globalPool.Get() + defer conn.Close() + + _, err := conn.Do("LPUSH", lastvaluesPrefix + deveui, value) + + if err != nil { + return + } + + _, err2 := conn.Do("LTRIM", lastvaluesPrefix + deveui, 0, 4) + + if err2 != nil { + return + } + + // we set an expiration time of one hour + _, err3 := conn.Do("EXPIRE", lastvaluesPrefix + deveui, 3600) + + if err3 != nil { + return + } +} + +func getDevAlias(deveui string) string { + res := deveui + + if deveui == "" { + return res + } + + + conn := globalPool.Get() + defer conn.Close() + + alias, err := redis.String(conn.Do("HGET", devPrefix+deveui, "alias")) + if err == nil { + res = alias + } else { + res = deveui + } + + return res +}