package main import ( "bufio" "bytes" "fmt" "github.com/gomodule/redigo/redis" "io/ioutil" "log" "net/http" "net/smtp" "strconv" "strings" "time" ) func sendEmailAccu(username string, alias string, deveui string, accu_percent string) { c, err := smtp.Dial("127.0.0.1:25") if err != nil { log.Fatal(err) } defer c.Close() // Set the sender and recipient. c.Mail("info@mini-beieli.ch") c.Rcpt(username) // Send the email body. wc, err := c.Data() if err != nil { log.Fatal(err) } defer wc.Close() mail_message := "To: " + username + ` Subject: mini-beieli.ch: Bitte Akku laden (` + alias + `) Lieber Benutzer von mini-beieli.ch Der Akku von "` + alias + `" (DevEUI: ` + deveui + `) ist nur noch zu ` + accu_percent + ` Prozent geladen. Bitte bei nächster Gelegenheit laden. Mit freundlichen Grüssen -- mini-beieli.ch` buf := bytes.NewBufferString(mail_message) if _, err = buf.WriteTo(wc); err != nil { log.Fatal(err) } } func sendEmailAbo(username string, alias string, deveui string, days_left int) { c, err := smtp.Dial("127.0.0.1:25") if err != nil { log.Fatal(err) } defer c.Close() // Set the sender and recipient. c.Mail("info@mini-beieli.ch") c.Rcpt(username) // Send the email body. wc, err := c.Data() if err != nil { log.Fatal(err) } defer wc.Close() mail_message := "To: " + username + ` Subject: mini-beieli.ch: Abo laeuft ab (` + alias + `) Lieber Benutzer von mini-beieli.ch Das Abo von "` + alias + `" (DevEUI: ` + deveui + `) laeuft in ` + strconv.Itoa(days_left) + ` Tagen ab. Bitte Abo verlaengern auf https://mini-beieli.ch Mit freundlichen Grüssen -- mini-beieli.ch` buf := bytes.NewBufferString(mail_message) if _, err = buf.WriteTo(wc); err != nil { log.Fatal(err) } } 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 } 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 getLastMetrics(deveui string) OneMetric { var res OneMetric url := "http://localhost:9999/api/v2/query?org=beieliorg" data := []byte(fmt.Sprintf(`from(bucket:"beielibucket") |> range(start:-5d) |> filter(fn: (r) => r.deveui == "%s") |> filter(fn: (r) => r._field == "vp") |> last() |> yield(name: "last")`, deveui)) req, err := http.NewRequest("POST", url, bytes.NewBuffer(data)) if err != nil { log.Fatal("Error reading request. ", err) } // Set headers req.Header.Set("Authorization", "Token OzSltiHLmm3WCaFPI0DrK0VFAVCiqcORNrGsgHO43jb0qodyVGAumJQ3HRtkaze53BVR3AmHqfkmrwrjq-xTyA==") 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(), ",") if (len(s) >= 7) && !(strings.HasPrefix(s[5], "_")) { mytime, err := time.Parse(time.RFC3339, s[5]) if err != nil { continue } res.Timestamp = mytime.In(location).Format("02.01.2006 15:04") value := s[6] field := s[7] if field == "vp" { res.BatteryPercent = value } } } res.Deveui = deveui res.Alias = getDevAlias(deveui) res.ActiveUntil = getActiveUntil(deveui) res.DaysUntilDeactivated = CalcDaysUntil(res.ActiveUntil) return res } 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 vp < 90 { 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) sendEmailAccu("joerg.lehmann@nbit.ch", alias, d, last_metric.BatteryPercent) } } // 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("joerg.lehmann@nbit.ch", alias, d, last_metric.DaysUntilDeactivated) } } } } logit("Done with check_battery...") }