implement alerting, first iteration

This commit is contained in:
Joerg Lehmann 2021-06-02 11:01:37 +02:00
parent 4960fc86e9
commit f66060bc7e
4 changed files with 360 additions and 2 deletions

108
alert.go Normal file
View File

@ -0,0 +1,108 @@
package main
import (
"fmt"
"io/ioutil"
"log"
"net/http"
"net/url"
"strings"
"time"
)
func stopAlerting(deveui string) {
myurl := fmt.Sprintf("https://proxy1.lpn.swisscom.ch/thingpark/lrc/rest/downlink?DevEUI=%s&FPort=2&Payload=0201", deveui)
req, err := http.NewRequest("POST", myurl, nil)
if err != nil {
log.Fatal("Error reading request. ", err)
}
req.Header.Set("Content-type", "application/x-www-form-urlencoded")
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 sendSMS(phonenumber string, alertMessage string) {
myurl := fmt.Sprintf("https://api.smsapi.com/sms.do?to=%s&message=%s&from=mini-beieli&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)
smsnumber := getSmsnumber(deveui)
alarmactive := getDevAlarmactive(deveui)
fmt.Printf("sendAlert: deveui=%s, smsnumber=%s, alarmactive=%s\n", deveui, smsnumber, alarmactive)
if (smsnumber != "") && (alarmactive == "1") {
// we strip of the leading +
smsnumber = strings.Replace(smsnumber, "+", "", -1)
sendSMS(smsnumber, alertMessage)
} else {
email := getEmail(deveui)
fmt.Printf("sendEmail: deveui=%s, email=%s\n", deveui, email)
sendEmail(email, alertMessage)
}
}
func DispatchAlert(deveui string, alertMessage string) {
// first let's stop the alerting cyle on the Lora Network (every minute for one hour!)
stopAlerting(deveui)
// we check if deveui exists
if !(checkDevExists(deveui)) {
fmt.Printf("Error: Deveui %s does not exist!", deveui)
return
}
// then we check if it expired
if AboExpired(deveui) {
fmt.Printf("Error: Abo for Deveui %s is expired!", deveui)
return
}
// then we check that an alert was not already sent out recently
if AlertAlreadySentRecently(deveui) {
fmt.Printf("Error: Alert for Deveui %s has already been sent!", deveui)
return
}
// then we make an entry that an alert was sent, this will expire automatically
AddAlertAlreadySentRecently(deveui)
// then we send the alert
sendAlert(deveui, alertMessage)
}

37
mail.go Normal file
View File

@ -0,0 +1,37 @@
package main
import (
"bytes"
"log"
"net/smtp"
)
func sendEmail(username, message 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@wo-bisch.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: ` + message + `
Lieber Benutzer von wo-bisch.ch
` + message + `
--
wo-bisch.ch`
buf := bytes.NewBufferString(mail_message)
if _, err = buf.WriteTo(wc); err != nil {
log.Fatal(err)
}
}

202
persistence.go Normal file
View File

@ -0,0 +1,202 @@
package main
import (
"github.com/gomodule/redigo/redis"
"log"
"time"
)
var globalPool *redis.Pool
const alertsentPrefix string = "alertsent:"
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)
}
func closeDB() {
globalPool.Close()
}
func getActiveUntil(deveui string) string {
res := ""
if deveui == "" {
return res
}
conn := globalPool.Get()
defer conn.Close()
activeuntil, err := redis.String(conn.Do("HGET", devPrefix+deveui, "active_until"))
if err == nil {
res = activeuntil
} else {
log.Print(err)
}
return res
}
func AboExpired(deveui string) bool {
active_until := getActiveUntil(deveui)
layout := "02.01.2006"
t, _ := time.Parse(layout, active_until)
return t.Before(time.Now())
}
func checkDevExists(deveui string) bool {
conn := globalPool.Get()
defer conn.Close()
_, err := redis.String(conn.Do("GET", devPrefix+deveui))
if err == redis.ErrNil {
return false
} else if err != nil {
return true
}
return false
}
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
}
func getDevAlarmactive(deveui string) string {
res := "0"
if deveui == "" {
return res
}
conn := globalPool.Get()
defer conn.Close()
alarmactive, err := redis.String(conn.Do("HGET", devPrefix+deveui, "alarmactive"))
if err == nil {
res = alarmactive
}
return res
}
func getSmsnumber(deveui string) string {
res := ""
if deveui == "" {
return res
}
conn := globalPool.Get()
defer conn.Close()
smsnumber, err := redis.String(conn.Do("HGET", devPrefix+deveui, "smsnumber"))
if err == nil {
res = smsnumber
}
return res
}
func getEmail(deveui string) string {
res := ""
if deveui == "" {
return res
}
conn := globalPool.Get()
defer conn.Close()
email, err := redis.String(conn.Do("HGET", devPrefix+deveui, "email"))
if err == nil {
res = email
}
return res
}
func AlertAlreadySentRecently(deveui string) bool {
conn := globalPool.Get()
defer conn.Close()
exists, _ := redis.Bool(conn.Do("EXISTS", alertsentPrefix+deveui))
return exists
}
func AddAlertAlreadySentRecently(deveui string) {
conn := globalPool.Get()
defer conn.Close()
_, err := conn.Do("SET", alertsentPrefix+deveui, "1")
if err != nil {
return
}
// we set an expiration time of three hours
_, err1 := conn.Do("EXPIRE", alertsentPrefix+deveui, 600)
if err1 != nil {
return
}
}

View File

@ -90,12 +90,20 @@ func DecodePayload(s string, deveui string) {
log.Printf("AlarmBat: %d", pl.AlarmBat) log.Printf("AlarmBat: %d", pl.AlarmBat)
log.Printf("Flag: %d", pl.Flag) log.Printf("Flag: %d", pl.Flag)
vbat := (pl.AlarmBat & 0x3fff) // Battery Voltage in mV vbat := (pl.AlarmBat & 0x3fff) // Battery Voltage in mV
fw := 160 + (pl.Flag & 0x1f) // Firmware version; 5 bits fw := 160 + (pl.Flag & 0x1f) // Firmware version; 5 bits
alarm := (pl.AlarmBat & 0x4000) == 0x4000 // Alarm Bit
log.Printf("AlarmBit: %v", alarm)
mystring := fmt.Sprintf("measurement,deveui=%s lat=%.5f,lon=%.5f,vbat=%d,fw=%d %d\n", deveui, float32(pl.Latitude)/1000000.0, float32(pl.Longitude)/1000000.0, vbat, fw, (time.Now().Unix() * 1000 * 1000 * 1000)) mystring := fmt.Sprintf("measurement,deveui=%s lat=%.5f,lon=%.5f,vbat=%d,fw=%d %d\n", deveui, float32(pl.Latitude)/1000000.0, float32(pl.Longitude)/1000000.0, vbat, fw, (time.Now().Unix() * 1000 * 1000 * 1000))
WriteStringToFile(mystring, deveui) WriteStringToFile(mystring, deveui)
// we send an alert, when the alert bit is set
if alarm {
log.Printf("DispatchAlert (button pressed) for %s\n", deveui)
DispatchAlert(deveui, "Alert button pressed!")
}
} else { } else {
log.Printf("Payload is not >= 11 bytes (22 chars): %s", s) log.Printf("Payload is not >= 11 bytes (22 chars): %s", s)
@ -103,6 +111,9 @@ func DecodePayload(s string, deveui string) {
} }
func main() { func main() {
// Init Redis
initDB()
// Open Output File // Open Output File
f, err := os.OpenFile(outputfile, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644) f, err := os.OpenFile(outputfile, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
file = f file = f