implement alerting, first iteration
This commit is contained in:
parent
4960fc86e9
commit
f66060bc7e
|
|
@ -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)
|
||||||
|
}
|
||||||
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue