package main import ( "bufio" "bytes" "fmt" "html/template" "io/ioutil" "log" "net/http" "os" "strconv" "strings" "time" ) type OneMetric struct { Deveui string DeveuiJS template.JS Alias string Readonly bool Smsalarmactive string Smsnumber string Emailalarmactive string Email string Greenzone string Timestamp string PosTimestamp string Lat string Lon string LatJS template.JS LonJS template.JS Vbat string Fw string BatteryPercent string BatteryPercentHTML template.HTML ActiveUntil string DaysUntilDeactivated int // berechneter Wert } // global variables var INFLUX_RO_TOKEN = os.Getenv("INFLUX_RO_TOKEN") // metrics handler func string2float64(s string) float64 { f, err := strconv.ParseFloat(s, 64) if err == nil { return f } return 0 } func metricsHandler(response http.ResponseWriter, request *http.Request) { name := getUserName(request) if name != "" { deveui, ok := request.URL.Query()["deveui"] if !ok || len(deveui[0]) < 1 { log.Println("Url Param 'deveui' is missing") fmt.Fprintf(response, "{ \"msg\": \"error: deveui must be specified in URL\" }") return } // Query()["deveui"] will return an array of items, // we only want the single item. mydeveui := deveui[0] if !(Contains(getMyDevs(name), mydeveui)) { log.Println("specified 'deveui' does not belong to this user") fmt.Fprintf(response, "{ \"msg\": \"error: specified deveui does not belong to this user\" }") return } if AboExpired(mydeveui) { log.Println("specified 'deveui' has an expired abo") fmt.Fprintf(response, "{ \"msg\": \"specified deveui has an expired abo\" }") return } log.Println("Url Param 'deveui' is: " + string(mydeveui)) // Format of start and stop: YYYY-MM-DDTHH:MI:SSZ stop, ok := request.URL.Query()["stop"] var mystop string if !ok || len(stop[0]) < 1 { log.Println("Url Param 'stop' is missing, set it to now") mystop = time.Now().Format("2006-01-02T15:04:05Z") } if ok { mystop = stop[0] } layout := "2006-01-02T15:04:05Z" stopDate, err := time.Parse(layout, mystop) if err != nil { fmt.Println(err) } start, ok := request.URL.Query()["start"] var mystart string if !ok || len(start[0]) < 1 { log.Println("Url Param 'start' is missing, set it to stop minus one day") t := stopDate.AddDate(0, 0, -1) mystart = t.Format("2006-01-02T15:04:05Z") } if ok { mystart = start[0] } url := "http://localhost:8086/api/v2/query?org=wobischorg" data := []byte(fmt.Sprintf(`from(bucket:"wobischbucket") |> range(start: %s, stop: %s) |> filter(fn: (r) => r._measurement == "measurement") |> filter(fn: (r) => r.deveui == "%s") |> pivot(rowKey:["_time"], columnKey: ["_field"], valueColumn: "_value") |> filter(fn: (r) => r.lat != 0)`, mystart, mystop, mydeveui)) 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 "+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))) first := true var lat float64 var lon float64 var min_lat float64 = 9999999999.9 var min_lon float64 = 9999999999.9 var max_lat float64 = 0 var max_lon float64 = 0 fmt.Fprintf(response, "{\n") fmt.Fprintf(response, " \"data\": [\n") for scanner.Scan() { s := strings.Split(scanner.Text(), ",") fmt.Printf("Scanned Line: %v, %d elements\n", s, len(s)) if (len(s) >= 12) && !(strings.HasPrefix(s[5], "_")) { t, err := time.Parse(time.RFC3339, s[5]) if err != nil { fmt.Printf("error converting time: %s\n", s[5]) continue } a := t.Unix() lat = string2float64(s[len(s)-3]) lon = string2float64(s[len(s)-2]) if (lat == 0) || (lon == 0) { fmt.Println("skip 0 value for lon/lat") } else { if lat < min_lat { min_lat = lat } if lon < min_lon { min_lon = lon } if lat > max_lat { max_lat = lat } if lon > max_lon { max_lon = lon } if !(first) { fmt.Fprintf(response, " ,") } else { first = false fmt.Fprintf(response, " ") } fmt.Fprintf(response, "[%d, %s, %s, %s, %d]\n", a, s[len(s)-3], s[len(s)-2], s[len(s)-1], vbat2percent(s[len(s)-1])) } } } fmt.Fprintf(response, " ],\n") fmt.Fprintf(response, " \"min_lat\": %f,\n", min_lat) fmt.Fprintf(response, " \"min_lon\": %f,\n", min_lon) fmt.Fprintf(response, " \"max_lat\": %f,\n", max_lat) fmt.Fprintf(response, " \"max_lon\": %f\n", max_lon) fmt.Fprintf(response, "}\n") } else { fmt.Fprintf(response, "{ \"msg\": \"Only available for logged in users\" }") } } func downloadmetricsHandler(response http.ResponseWriter, request *http.Request) { name := getUserName(request) if name != "" { deveui, ok := request.URL.Query()["deveui"] if !ok || len(deveui[0]) < 1 { log.Println("Url Param 'deveui' is missing") fmt.Fprintf(response, "Error: deveui is missing") return } // Query()["deveui"] will return an array of items, // we only want the single item. mydeveui := deveui[0] if !(Contains(getMyDevs(name), mydeveui)) { log.Println("specified 'deveui' does not belong to this user") fmt.Fprintf(response, "Error: specified deveui does not belong to this user") return } if AboExpired(mydeveui) { log.Println("specified 'deveui' has an expired abo") fmt.Fprintf(response, "Error: specified deveui has an expired abo") return } log.Println("Url Param 'deveui' is: " + string(mydeveui)) // Format of start and stop: YYYY-MM-DDTHH:MI:SSZ stop, ok := request.URL.Query()["stop"] var mystop string if !ok || len(stop[0]) < 1 { log.Println("Url Param 'stop' is missing, set it to now") mystop = time.Now().Format("2006-01-02T15:04:05Z") } if ok { mystop = stop[0] } layout := "2006-01-02T15:04:05Z" stopDate, err := time.Parse(layout, mystop) if err != nil { fmt.Println(err) } start, ok := request.URL.Query()["start"] var mystart string if !ok || len(start[0]) < 1 { log.Println("Url Param 'start' is missing, set it to stop minus one day") t := stopDate.AddDate(0, 0, -1) mystart = t.Format("2006-01-02T15:04:05Z") } if ok { mystart = start[0] } url := "http://localhost:8086/api/v2/query?org=wobischorg" data := []byte(fmt.Sprintf(`from(bucket:"wobischbucket") |> range(start: %s, stop: %s) |> filter(fn: (r) => r._measurement == "measurement") |> filter(fn: (r) => r.deveui == "%s") |> filter(fn: (r) => r._field == "lon" or r._field == "lat" or r._field == "flags" or r._field == "vbat") |> pivot(rowKey:["_time"], columnKey: ["_field"], valueColumn: "_value")`, mystart, mystop, mydeveui)) 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 "+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)) location, err := time.LoadLocation("Europe/Zurich") scanner := bufio.NewScanner(strings.NewReader(string(body))) for scanner.Scan() { s := strings.Split(scanner.Text(), ",") fmt.Printf("Scanned Line: %v, elements: %d\n", s, len(s)) if (len(s) >= 11) && !(strings.HasPrefix(s[5], "_")) { s_flags := "" lat := s[len(s)-3] lon := s[len(s)-2] if lat == "0" { s_flags = "NOGPS" } else { s_flags = "GPS" } if len(s) == 12 { flags, _ := strconv.Atoi(s[len(s)-4]) if flags&1 == 1 { s_flags = s_flags + "+ALARM_BUTTON" } if flags&2 == 2 && lat != "0" { s_flags = s_flags + "+OUTSIDE_GREENZONE" } } t, err := time.Parse(time.RFC3339, s[5]) if err != nil { fmt.Printf("error converting time: %s\n", s[5]) continue } mytime := t.In(location).Format("02.01.2006 15:04") vbat := s[len(s)-1] percent := vbat2percent(vbat) fmt.Fprintf(response, "\"%s\",%s,%s,%d,%s\n", mytime, lon, lat, percent, s_flags) } } } else { fmt.Fprintf(response, "Only available for logged in Users") } } 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 vbat2html(vbat string) string { percent := vbat2percent(vbat) fa_battery_string := "" icon_color := "" if percent <= 20 { fa_battery_string = "fa-battery-empty" icon_color = "has-background-danger-dark has-text-white pl-2 pr-2" } else if percent <= 40 { fa_battery_string = "fa-battery-quarter" icon_color = "has-text-success pl-2 pr-2" } else if percent <= 60 { fa_battery_string = "fa-battery-half" icon_color = "has-text-success pl-2 pr-2" } else if percent <= 80 { fa_battery_string = "fa-battery-three-quarters" icon_color = "has-text-success pl-2 pr-2" } else { fa_battery_string = "fa-battery-full" icon_color = "has-text-success pl-2 pr-2" } return " " + strconv.Itoa(percent) + " %" } 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:8086/api/v2/query?org=wobischorg" data := []byte(fmt.Sprintf(`from(bucket:"wobischbucket") |> range(start:-365d) |> filter(fn: (r) => r._measurement == "measurement" and r.deveui == "%s") |> pivot(rowKey:["_time"], columnKey: ["_field"], valueColumn: "_value") |> filter(fn: (r) => r.lat != 0) |> sort(columns: ["_time"], desc: false) |> last(column: "_time")`, 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 "+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 2:", 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("BlaBla: %v, elements: %d\n", s, len(s)) if (len(s) >= 12) && !(strings.HasPrefix(s[5], "_")) { t, err := time.Parse(time.RFC3339, s[5]) if err != nil { fmt.Printf("error converting time: %s\n", s[5]) continue } res.Fw = s[len(s)-4] res.Timestamp = t.In(location).Format("02.01.2006 15:04") if s[9] != "0" { res.Lat = s[len(s)-3] res.PosTimestamp = t.In(location).Format("02.01.2006 15:04") } if s[10] != "0" { res.Lon = s[len(s)-2] res.PosTimestamp = t.In(location).Format("02.01.2006 15:04") } } } data2 := []byte(fmt.Sprintf(`from(bucket:"wobischbucket") |> range(start:-365d) |> filter(fn: (r) => r._measurement == "measurement" and r.deveui == "%s") |> pivot(rowKey:["_time"], columnKey: ["_field"], valueColumn: "_value") |> sort(columns: ["_time"], desc: false) |> last(column: "_time")`, deveui)) req2, err := http.NewRequest("POST", url, bytes.NewBuffer(data2)) if err != nil { log.Fatal("Error reading request. ", err) } // Set headers req2.Header.Set("Authorization", "Token "+INFLUX_RO_TOKEN) req2.Header.Set("accept", "application/csv") req2.Header.Set("content-type", "application/vnd.flux") // Send request resp2, err := client.Do(req2) if err != nil { log.Fatal("Error reading response. ", err) } defer resp2.Body.Close() fmt.Println("response Status:", resp2.Status) fmt.Println("response Headers:", resp2.Header) body2, err := ioutil.ReadAll(resp2.Body) if err != nil { log.Fatal("Error reading body. ", err) } fmt.Println("response Body 2:", string(body2)) scanner2 := bufio.NewScanner(strings.NewReader(string(body2))) for scanner2.Scan() { s := strings.Split(scanner2.Text(), ",") fmt.Printf("BlaBla: %v, elements: %d\n", s, len(s)) if (len(s) >= 12) && !(strings.HasPrefix(s[5], "_")) { t, err := time.Parse(time.RFC3339, s[5]) if err != nil { fmt.Printf("error converting time: %s\n", s[5]) continue } res.BatteryPercent = strconv.Itoa(vbat2percent(s[len(s)-1])) res.BatteryPercentHTML = template.HTML(vbat2html(s[len(s)-1])) } } res.Deveui = deveui res.DeveuiJS = template.JS(deveui) res.LatJS = template.JS(res.Lat) res.LonJS = template.JS(res.Lon) res.Alias = getDevAlias(deveui) res.Smsalarmactive = getDevSmsalarmactive(deveui) res.Smsnumber = getDevSmsnumber(deveui) res.Emailalarmactive = getDevEmailalarmactive(deveui) res.Email = getDevEmail(deveui) res.Greenzone = getDevGreenzone(deveui) res.Readonly = false res.ActiveUntil = getActiveUntil(deveui) res.DaysUntilDeactivated = CalcDaysUntil(res.ActiveUntil) fmt.Printf("Last Metrics: %v\n", res) return res }