monitor program to be used from cron, checking battery and abo
This commit is contained in:
parent
6e52aa9acd
commit
bc3098c9c8
|
|
@ -2,3 +2,4 @@ mini-beieli-web
|
||||||
nohup.out
|
nohup.out
|
||||||
database/
|
database/
|
||||||
node_modules/
|
node_modules/
|
||||||
|
check_mini_beieli_nodes/check_mini_beieli_nodes
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,358 @@
|
||||||
|
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...")
|
||||||
|
}
|
||||||
|
|
@ -82,6 +82,24 @@ func closeDB() {
|
||||||
globalPool.Close()
|
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 updateScaleSettings(scaleSettings Dev) error {
|
func updateScaleSettings(scaleSettings Dev) error {
|
||||||
conn := globalPool.Get()
|
conn := globalPool.Get()
|
||||||
defer conn.Close()
|
defer conn.Close()
|
||||||
|
|
@ -100,7 +118,7 @@ func checkUserAvailable(username string) bool {
|
||||||
conn := globalPool.Get()
|
conn := globalPool.Get()
|
||||||
defer conn.Close()
|
defer conn.Close()
|
||||||
|
|
||||||
_, err := redis.String(conn.Do("GET", userPrefix+username))
|
_, err := conn.Do("HGETALL", userPrefix+username)
|
||||||
if err == redis.ErrNil {
|
if err == redis.ErrNil {
|
||||||
logit("User does not exist and is therefore available:" + username)
|
logit("User does not exist and is therefore available:" + username)
|
||||||
return true
|
return true
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue