From 736fc6b05892ab830f3e6d62e8bc5a042ccd90e6 Mon Sep 17 00:00:00 2001 From: Joerg Lehmann Date: Thu, 14 Jul 2022 19:38:33 +0200 Subject: [PATCH] integrate check_nodes in wo-bisch-web --- check_nodes.go | 122 +++++++++++++ check_nodes/main.go | 419 -------------------------------------------- main.go | 1 + persistence.go | 36 ++++ 4 files changed, 159 insertions(+), 419 deletions(-) create mode 100644 check_nodes.go delete mode 100644 check_nodes/main.go diff --git a/check_nodes.go b/check_nodes.go new file mode 100644 index 0000000..347b374 --- /dev/null +++ b/check_nodes.go @@ -0,0 +1,122 @@ +package main + +import ( + "fmt" + "net/http" + "strconv" + "strings" +) + +func sendEmailAccu(username string, alias string, deveui string, accu_percent string, threshold int, level string) { + fmt.Printf("SEND EMAIL ACCU (%s) - %s:%s\n", level, username, deveui) + mail_message := "To: " + username + ` +From: info@wo-bisch.ch +Subject: ` + level + ` - wo-bisch.ch: Akku Ladezustand (` + alias + `) + +Lieber Benutzer von wo-bisch.ch + +Der Akku von "` + alias + `" (DevEUI: ` + deveui + `) ist noch zu ` + accu_percent + ` Prozent geladen. + +Bitte rechtzeitig wieder laden! Bei Unterschreitung von 5% Ladung erscheint die letzte Warnung. + +Mit freundlichen Grüssen +-- +wo-bisch.ch` + + sendEmail(username, "mail@wo-bisch.ch", mail_message) +} + +func sendEmailAbo(username string, alias string, deveui string, days_left int, level string) { + var ablauftext string + if days_left == 0 { + fmt.Printf("SEND EMAIL ABO (%s) - %s:%s\n", level, username, deveui) + ablauftext = "Das Abo von \"" + alias + "\" (DevEUI: " + deveui + ") ist abgelaufen." + } else if days_left > 0 { + fmt.Printf("SEND EMAIL ABO (%s) - %s:%s\n", level, username, deveui) + ablauftext = "Das Abo von \"" + alias + "\" (DevEUI: " + deveui + ") laeuft in " + strconv.Itoa(days_left) + " Tagen ab." + } else { + fmt.Printf("DO NOT SEND EMAIL ABO (%s) - %s:%s\n", level, username, deveui) + return + } + mail_message := "To: " + username + ` +From: info@wo-bisch.ch +Subject: ` + level + ` - wo-bisch.ch: Abo laeuft ab (` + alias + `) + +Lieber Benutzer von wo-bisch.ch + +` + ablauftext + `Das Abo von "` + alias + `" (DevEUI: ` + deveui + `) laeuft in ` + strconv.Itoa(days_left) + ` Tagen ab. + +Bitte Abo verlaengern auf https://wo-bisch.ch + +Mit freundlichen Grüssen +-- +wo-bisch.ch` + + sendEmail(username, "mail@wo-bisch.ch", mail_message) +} + +func CheckThreshold(d string, vp int, u2 string, last_metric OneMetric, info_threshold int, warning_threshold int, alert_threshold int) bool { + var alias string + if vp <= info_threshold { + alias = getDevAlias(d) + } + if vp <= alert_threshold { + if AlarmNotAlreadySent("alarm_sent_accu:", d, u2, alert_threshold) { + sendEmailAccu(u2, alias, d, last_metric.BatteryPercent, alert_threshold, "ALARM") + InsertAlert("alarm_sent_accu:", d, u2, alert_threshold) + } + return false + } + if vp <= warning_threshold { + if AlarmNotAlreadySent("alarm_sent_accu:", d, u2, warning_threshold) { + sendEmailAccu(u2, alias, d, last_metric.BatteryPercent, warning_threshold, "WARNING") + InsertAlert("alarm_sent_accu:", d, u2, warning_threshold) + } + return false + } + if vp <= info_threshold { + if AlarmNotAlreadySent("alarm_sent_accu:", d, u2, info_threshold) { + sendEmailAccu(u2, alias, d, last_metric.BatteryPercent, alert_threshold, "INFO") + InsertAlert("alarm_sent_accu:", d, u2, info_threshold) + } + return false + } + return true +} + +func checkNodes() { + logit("Starting check_battery...") + + users := getUsers() + for _, u := range users { + //fmt.Println(u) + u2 := u[5:] + my_devs := getMyDevs(u2) + for _, d := range my_devs { + fmt.Printf("%s:%s\n", u2, d) + if !strings.HasPrefix(d, "@") { + last_metric := getLastMetrics(d) + // Zuerst der Batteriealarm + if last_metric.BatteryPercent != "" { + fmt.Printf("%s:%s:%s Percent:%s:%d\n", u2, d, last_metric.BatteryPercent, last_metric.ActiveUntil, last_metric.DaysUntilDeactivated) + vp, _ := strconv.Atoi(last_metric.BatteryPercent) + if CheckThreshold(d, vp, u2, last_metric, 20, 10, 5) { + DeleteAlert("alarm_sent_accu:", d, u2) + } + } + // Jetzt der Alarm wegen der Abodauer + if last_metric.DaysUntilDeactivated < 30 { + fmt.Printf("SEND EMAIL %s:%s:%s Percent:%s:%d\n", u2, d, last_metric.BatteryPercent, last_metric.ActiveUntil, last_metric.DaysUntilDeactivated) + alias := getDevAlias(d) + sendEmailAbo(u2, alias, d, last_metric.DaysUntilDeactivated, "INFO") + } + } + } + } + logit("Done with check_battery...") +} + +func checkNodesHandler(w http.ResponseWriter, req *http.Request) { + checkNodes() + w.WriteHeader(http.StatusOK) +} diff --git a/check_nodes/main.go b/check_nodes/main.go deleted file mode 100644 index 3450d9d..0000000 --- a/check_nodes/main.go +++ /dev/null @@ -1,419 +0,0 @@ -package main - -import ( - "bufio" - "bytes" - "fmt" - "github.com/gomodule/redigo/redis" - "github.com/jordan-wright/email" - "io/ioutil" - "log" - "net/http" - "os" - "strconv" - "strings" - "time" -) - -func sendEmailAccu(username string, alias string, deveui string, accu_percent string, threshold int, level string) { - fmt.Printf("SEND EMAIL ACCU (%s) - %s:%s\n", level, username, deveui) - mail_message := `Lieber Benutzer von wo-bisch.ch - -Der Akku von "` + alias + `" (DevEUI: ` + deveui + `) ist noch zu ` + accu_percent + ` Prozent geladen. - -Bitte rechtzeitig wieder laden! Bei Unterschreitung von 5% Ladung erscheint die letzte Warnung. - -Mit freundlichen Grüssen --- -wo-bisch.ch` - - e := email.NewEmail() - e.From = "wo-bisch.ch " - e.To = []string{username} - e.Bcc = []string{"joerg.lehmann@nbit.ch"} - e.Subject = level + " - wo-bisch.ch: Akku Ladezustand (" + alias + ")" - e.Text = []byte(mail_message) - e.Send("127.0.0.1:25", nil) -} - -func sendEmailAbo(username string, alias string, deveui string, days_left int, level string) { - fmt.Printf("SEND EMAIL ABO (%s) - %s:%s\n", level, username, deveui) - mail_message := `Lieber Benutzer von wo-bisch.ch - -Das Abo von "` + alias + `" (DevEUI: ` + deveui + `) laeuft in ` + strconv.Itoa(days_left) + ` Tagen ab. - -Bitte Abo verlaengern auf https://wo-bisch.ch - -Mit freundlichen Grüssen --- -wo-bisch.ch` - - e := email.NewEmail() - e.From = "wo-bisch.ch " - e.To = []string{username} - e.Bcc = []string{"joerg.lehmann@nbit.ch"} - e.Subject = level + " - wo-bisch.ch: Abo laeuft ab (" + alias + ")" - e.Text = []byte(mail_message) - e.Send("127.0.0.1:25", nil) -} - -var globalPool *redis.Pool - -const userPrefix string = "user:" -const devPrefix string = "dev:" -const confirmPrefix string = "confirm:" - -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 - s, err := redis.String(c.Do("PING")) - if err != nil { - return err - } - - logit("PING Response = " + s) - // Output: PONG - - return nil -} - -type Dev struct { - Deveui string - Alias string - Alarmactive string - Smsnumber string - ActiveUntil string // Abo bezahlt bis TT.MM.YYYY -} - -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) -} - -func closeDB() { - globalPool.Close() -} - -func getUsers() []string { - res := []string{} - - conn := globalPool.Get() - defer conn.Close() - - //logit("getUsers") - users, err := redis.Strings(conn.Do("KEYS", userPrefix+"*")) - if err == nil { - //logit("getUsers successful!") - res = users - } else { - log.Print(err) - } - - return res -} - -func getMyDevs(username string) []string { - res := []string{} - - if username == "" { - return res - } - - conn := globalPool.Get() - defer conn.Close() - - //logit("getMyDevs: User: " + username) - mydevs, err := redis.String(conn.Do("HGET", userPrefix+username, "my_devs")) - if err == nil { - //logit("getMyDevs: mydevs: " + mydevs) - res = strings.Split(mydevs, ",") - } else { - log.Print(err) - } - - return res -} - -func getDevAlias(deveui string) string { - res := deveui - - if deveui == "" { - return res - } - - conn := globalPool.Get() - defer conn.Close() - - //logit("getDevAlias: Deveui: " + deveui) - alias, err := redis.String(conn.Do("HGET", devPrefix+deveui, "alias")) - if err == nil { - //logit("getDevAlias: alias: " + alias) - res = alias - } else { - log.Print(err) - } - - return res -} - -func getActiveUntil(deveui string) string { - res := "" - - if deveui == "" { - return res - } - - conn := globalPool.Get() - defer conn.Close() - - //logit("getActiveUntil: Deveui: " + deveui) - activeuntil, err := redis.String(conn.Do("HGET", devPrefix+deveui, "active_until")) - if err == nil { - //logit("getActiveUntil: activeuntil: " + activeuntil) - res = activeuntil - } else { - log.Print(err) - } - - return res -} - -func InsertAlert(prefix string, deveui string, email string, threshold int) { - conn := globalPool.Get() - defer conn.Close() - - _, err := conn.Do("SET", prefix+deveui+":"+email, threshold) - if err != nil { - logit("InsertAlert: Error inserting: " + prefix + deveui + ":" + email) - } -} - -func DeleteAlert(prefix string, deveui string, email string) { - conn := globalPool.Get() - defer conn.Close() - - exists, _ := redis.Bool(conn.Do("EXISTS", prefix+deveui+":"+email)) - - if exists { - _, err := conn.Do("DEL", prefix+deveui+":"+email) - if err != nil { - logit("DeleteAlert: Error deleting: " + prefix + deveui + ":" + email) - } - } -} - -func AlarmNotAlreadySent(prefix string, deveui string, email string, threshold int) bool { - conn := globalPool.Get() - defer conn.Close() - - exists, _ := redis.Bool(conn.Do("EXISTS", prefix+deveui+":"+email)) - if !exists { - return true - } - alarm_threshold, _ := redis.Int(conn.Do("GET", prefix+deveui+":"+email)) - return threshold != alarm_threshold -} - -type OneMetric struct { - Deveui string - Alias string - Timestamp string - BatteryPercent string - ActiveUntil string - DaysUntilDeactivated int // berechneter Wert -} - -func CalcDaysUntil(mydate string) int { - var days int - layout := "02.01.2006" - t, err := time.Parse(layout, mydate) - - if err != nil { - days = 0 - } - days = int(t.Sub(time.Now()).Hours() / 24) - - return days -} - -func vbat2percent(vbat string) int { - i, err := strconv.Atoi(vbat) - res := 0 - if err == nil { - res = int(float64(i-3400) / 6.0) - fmt.Printf("vbat2percent Result in Percent: %d\n", res) - if res < 0 { - res = 0 - } else if res > 100 { - res = 100 - } - } - - return res -} - -func getLastMetrics(deveui string) OneMetric { - var res OneMetric - - res.BatteryPercent = "999" - - url := "http://localhost:8086/api/v2/query?org=wobischorg" - data := []byte(fmt.Sprintf(`from(bucket:"wobischbucket") - |> range(start:-5d) - |> filter(fn: (r) => r._measurement == "measurement" and r.deveui == "%s") - |> filter(fn: (r) => r._field == "vbat") - |> last(column: "_time")`, deveui)) - - req, err := http.NewRequest("POST", url, bytes.NewBuffer(data)) - if err != nil { - log.Fatal("Error reading request. ", err) - } - - // Set headers - var INFLUX_RO_TOKEN = os.Getenv("INFLUX_RO_TOKEN") - - req.Header.Set("Authorization", "Token "+INFLUX_RO_TOKEN) - req.Header.Set("accept", "application/csv") - req.Header.Set("content-type", "application/vnd.flux") - - // Set client timeout - client := &http.Client{Timeout: time.Second * 10} - - // Send request - resp, err := client.Do(req) - if err != nil { - log.Fatal("Error reading response. ", err) - } - defer resp.Body.Close() - - //fmt.Println("response Status:", resp.Status) - //fmt.Println("response Headers:", resp.Header) - - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - log.Fatal("Error reading body. ", err) - } - //fmt.Println("response Body:", string(body)) - - scanner := bufio.NewScanner(strings.NewReader(string(body))) - location, err := time.LoadLocation("Europe/Zurich") - for scanner.Scan() { - s := strings.Split(scanner.Text(), ",") - //fmt.Printf("s: %q\n", s) - if (len(s) >= 7) && !(strings.HasPrefix(s[3], "_")) { - t, err := time.Parse(time.RFC3339, s[5]) - if err != nil { - fmt.Printf("error converting time: %s\n", s[5]) - continue - } - - res.Timestamp = t.In(location).Format("02.01.2006 15:04") - res.BatteryPercent = strconv.Itoa(vbat2percent(s[6])) - fmt.Printf("vbat: %s\n", s[6]) - - } - - } - - res.Deveui = deveui - res.Alias = getDevAlias(deveui) - res.ActiveUntil = getActiveUntil(deveui) - res.DaysUntilDeactivated = CalcDaysUntil(res.ActiveUntil) - return res -} - -func CheckThreshold(d string, vp int, u2 string, last_metric OneMetric, info_threshold int, warning_threshold int, alert_threshold int) bool { - var alias string - if vp <= info_threshold { - alias = getDevAlias(d) - } - if vp <= alert_threshold { - if AlarmNotAlreadySent("alarm_sent_accu:", d, u2, alert_threshold) { - sendEmailAccu(u2, alias, d, last_metric.BatteryPercent, alert_threshold, "ALARM") - InsertAlert("alarm_sent_accu:", d, u2, alert_threshold) - } - return false - } - if vp <= warning_threshold { - if AlarmNotAlreadySent("alarm_sent_accu:", d, u2, warning_threshold) { - sendEmailAccu(u2, alias, d, last_metric.BatteryPercent, warning_threshold, "WARNING") - InsertAlert("alarm_sent_accu:", d, u2, warning_threshold) - } - return false - } - if vp <= info_threshold { - if AlarmNotAlreadySent("alarm_sent_accu:", d, u2, info_threshold) { - sendEmailAccu(u2, alias, d, last_metric.BatteryPercent, alert_threshold, "INFO") - InsertAlert("alarm_sent_accu:", d, u2, info_threshold) - } - return false - } - return true -} - -func logit(log_message string) { - log.Println(log_message) -} - -func main() { - logit("Starting check_battery...") - initDB() - defer closeDB() - - users := getUsers() - for _, u := range users { - //fmt.Println(u) - u2 := u[5:] - my_devs := getMyDevs(u2) - for _, d := range my_devs { - fmt.Printf("%s:%s\n", u2, d) - if !strings.HasPrefix(d, "@") { - last_metric := getLastMetrics(d) - // Zuerst der Batteriealarm - if last_metric.BatteryPercent != "" { - fmt.Printf("%s:%s:%s Percent:%s:%d\n", u2, d, last_metric.BatteryPercent, last_metric.ActiveUntil, last_metric.DaysUntilDeactivated) - vp, _ := strconv.Atoi(last_metric.BatteryPercent) - if CheckThreshold(d, vp, u2, last_metric, 20, 10, 5) { - DeleteAlert("alarm_sent_accu:", d, u2) - } - } - // Jetzt der Alarm wegen der Abodauer - if last_metric.DaysUntilDeactivated < 30 { - fmt.Printf("SEND EMAIL %s:%s:%s Percent:%s:%d\n", u2, d, last_metric.BatteryPercent, last_metric.ActiveUntil, last_metric.DaysUntilDeactivated) - alias := getDevAlias(d) - sendEmailAbo(u2, alias, d, last_metric.DaysUntilDeactivated, "INFO") - } - } - } - } - logit("Done with check_battery...") -} diff --git a/main.go b/main.go index c26e293..4816c7f 100644 --- a/main.go +++ b/main.go @@ -134,6 +134,7 @@ func main() { http.HandleFunc("/save_tracker_greenzone", save_tracker_greenzoneHandler) http.HandleFunc("/getstripepaymentintent", getstripepaymentintentHandler) http.HandleFunc("/stripewebhook", stripeWebhookHandler) + http.HandleFunc("/checknodes", checkNodesHandler) logit("Starting Web Application...") http.ListenAndServe(":4000", nil) diff --git a/persistence.go b/persistence.go index 87b62bc..0d158d3 100644 --- a/persistence.go +++ b/persistence.go @@ -521,3 +521,39 @@ func confirmUser(confirm_id string) bool { return true } + +func InsertAlert(prefix string, deveui string, email string, threshold int) { + conn := globalPool.Get() + defer conn.Close() + + _, err := conn.Do("SET", prefix+deveui+":"+email, threshold) + if err != nil { + logit("InsertAlert: Error inserting: " + prefix + deveui + ":" + email) + } +} + +func DeleteAlert(prefix string, deveui string, email string) { + conn := globalPool.Get() + defer conn.Close() + + exists, _ := redis.Bool(conn.Do("EXISTS", prefix+deveui+":"+email)) + + if exists { + _, err := conn.Do("DEL", prefix+deveui+":"+email) + if err != nil { + logit("DeleteAlert: Error deleting: " + prefix + deveui + ":" + email) + } + } +} + +func AlarmNotAlreadySent(prefix string, deveui string, email string, threshold int) bool { + conn := globalPool.Get() + defer conn.Close() + + exists, _ := redis.Bool(conn.Do("EXISTS", prefix+deveui+":"+email)) + if !exists { + return true + } + alarm_threshold, _ := redis.Int(conn.Do("GET", prefix+deveui+":"+email)) + return threshold != alarm_threshold +}