commit 7a7e5ffde1741c8a98535c660d3a79ea82443414 Author: Garrett Bjerkhoel Date: Fri Sep 30 00:08:55 2016 -0700 Build heroku compatible app diff --git a/integrations/promunifi/.gitignore b/integrations/promunifi/.gitignore new file mode 100644 index 00000000..4c49bd78 --- /dev/null +++ b/integrations/promunifi/.gitignore @@ -0,0 +1 @@ +.env diff --git a/integrations/promunifi/Procfile b/integrations/promunifi/Procfile new file mode 100644 index 00000000..a4fca2b1 --- /dev/null +++ b/integrations/promunifi/Procfile @@ -0,0 +1 @@ +worker: unifi diff --git a/integrations/promunifi/app.json b/integrations/promunifi/app.json new file mode 100644 index 00000000..deedb0cc --- /dev/null +++ b/integrations/promunifi/app.json @@ -0,0 +1,11 @@ +{ + "name": "unifi", + "description": "Data logger for Unifi controllers", + "keywords": [ + "go", + ], + "image": "heroku/go:1.7", + "mount_dir": "src/github.com/dewski/unifi", + "website": "http://github.com/dewski/unifi", + "repository": "http://github.com/dewski/unifi" +} diff --git a/integrations/promunifi/main.go b/integrations/promunifi/main.go new file mode 100644 index 00000000..36117d40 --- /dev/null +++ b/integrations/promunifi/main.go @@ -0,0 +1,251 @@ +package main + +import ( + "bytes" + "crypto/tls" + "encoding/json" + "errors" + "fmt" + "io/ioutil" + "log" + "net/http" + "net/http/cookiejar" + "os" + "time" + + influx "github.com/influxdata/influxdb/client/v2" +) + +var ( + API *http.Client + stats influx.Client +) + +type Response struct { + Data []Client + Meta struct { + Rc string + } +} + +type DpiStat struct { + App int64 + Cat int + RxBytes int64 + RxPackets int64 + TxBytes int64 + TxPackets int64 +} + +type Client struct { + ID string `json:"_id"` + IsGuestByUAP bool `json:"_is_guest_by_uap"` + IsGuestByUGW bool `json:"_is_guest_by_ugw"` + LastSeenByUAP int64 `json:"_last_seen_by_uap"` + LastSeenByUGW int64 `json:"_last_seen_by_ugw"` + UptimeByUAP int64 `json:"_uptime_by_uap"` + UptimeByUGW int64 `json:"_uptime_by_ugw"` + ApMac string `json:"ap_mac"` + AssocTime int64 `json:"assoc_time"` + Authorized bool + Bssid string + BytesR int `json:"bytes-r"` + Ccq int + Channel int + DpiStats []DpiStat `json:"dpi_stats"` + DpiStatsLastUpdated int64 `json:"dpi_stats_last_updated"` + Essid string + FirstSeen int64 `json:"first_seen"` + FixedIP string `json:"fixed_ip"` + Hostname string + GwMac string `json:"gw_mac"` + IdleTime int64 `json:"idle_time"` + Ip string + IsGuest bool `json:"is_guest"` + IsWired bool `json:"is_wired"` + LastSeen int64 `json:"last_seen"` + LatestAssocTime int64 `json:"latest_assoc_time"` + Mac string + Name string + Network string + NetworkID string `json:"network_id"` + Noise int + Oui string + PowersaveEnabled bool `json:"powersave_enabled"` + QosPolicyApplied bool `json:"qos_policy_applied"` + Radio string + RadioProto string `json:"radio_proto"` + RoamCount int `json:"roam_count"` + Rssi int + RxBytes int64 `json:"rx_bytes"` + RxBytesR int `json:"rx_bytes-r"` + RxPackets int64 `json:"rx_packets"` + RxRate int64 `json:"rx_rate"` + Signal int + SiteID string `json:"site_id"` + TxBytes int64 `json:"tx_bytes"` + TxBytesR int `json:"tx_bytes-r"` + TxPackets int64 `json:"tx_packets"` + TxPower int `json:"tx_power"` + TxRate int64 `json:"tx_rate"` + Uptime int64 + UserID string `json:"user_id"` + Vlan int +} + +func (c Client) Point() *influx.Point { + tags := map[string]string{ + "mac": c.Mac, + "user_id": c.UserID, + "site_id": c.SiteID, + "ip": c.Ip, + "essid": c.Essid, + "network": c.Network, + "ap_mac": c.ApMac, + "name": c.Name, + } + fields := map[string]interface{}{ + "is_guest_by_uap": c.IsGuestByUAP, + "is_guest_by_ugw": c.IsGuestByUGW, + "authorized": c.Authorized, + "last_seen_by_uap": c.LastSeenByUAP, + "last_seen_by_ugw": c.LastSeenByUGW, + "uptime_by_uap": c.UptimeByUAP, + "uptime_by_ugw": c.UptimeByUGW, + "assoc_time": c.AssocTime, + "bytes_r": c.BytesR, + "ccq": c.Ccq, + "channel": c.Channel, + "dpi_stats_last_updated": c.DpiStatsLastUpdated, + "first_seen": c.FirstSeen, + "idle_time": c.IdleTime, + "last_seen": c.LastSeen, + "latest_assoc_time": c.LatestAssocTime, + "noise": c.Noise, + "roam_count": c.RoamCount, + "rssi": c.Rssi, + "rx_bytes": c.RxBytes, + "rx_bytes_r": c.RxBytesR, + "rx_packets": c.RxPackets, + "rx_rate": c.RxRate, + "signal": c.Signal, + "tx_bytes": c.TxBytes, + "tx_bytes_r": c.TxBytesR, + "tx_packets": c.TxPackets, + "tx_power": c.TxPower, + "tx_rate": c.TxRate, + "uptime": c.Uptime, + "vlan": c.Vlan, + } + + pt, err := influx.NewPoint("client_state", tags, fields, time.Now()) + if err != nil { + return nil + } + + return pt +} + +func main() { + var err error + tickRate := os.Getenv("TICK_RATE") + interval, err := time.ParseDuration(tickRate) + if err != nil { + panic(err) + } + + API, err = login() + if err != nil { + panic(err) + } + + database := os.Getenv("INFLUXDB_DATABASE") + stats, err = influx.NewHTTPClient(influx.HTTPConfig{ + Addr: os.Getenv("INFLUXDB_ADDR"), + Username: os.Getenv("INFLUXDB_USERNAME"), + Password: os.Getenv("INFLUXDB_PASSWORD"), + }) + if err != nil { + panic(err) + } + + log.Printf("Starting to poll Unifi every %+v\n", interval) + for { + devices, err := fetch() + if err != nil { + log.Println(err) + } else { + bp, _ := influx.NewBatchPoints(influx.BatchPointsConfig{ + Database: database, + }) + + for _, device := range devices { + bp.AddPoint(device.Point()) + } + + err = stats.Write(bp) + if err != nil { + log.Println(err) + } + + log.Println("Logged client state...") + } + + time.Sleep(interval) + } +} + +func fetch() ([]Client, error) { + format := "https://%s:%s/api/s/default/stat/sta" + url := fmt.Sprintf(format, os.Getenv("UNIFI_ADDR"), os.Getenv("UNIFI_PORT")) + req, err := http.NewRequest("GET", url, nil) + req.Header.Add("Accept", "application/json") + resp, err := API.Do(req) + if err != nil { + return nil, err + } + + defer resp.Body.Close() + body, _ := ioutil.ReadAll(resp.Body) + response := &Response{} + err = json.Unmarshal(body, response) + if err != nil { + return nil, err + } + + return response.Data, nil +} + +func login() (*http.Client, error) { + url := fmt.Sprintf("https://%s:%s/api/login", os.Getenv("UNIFI_ADDR"), os.Getenv("UNIFI_PORT")) + auth := map[string]string{ + "username": os.Getenv("UNIFI_USERNAME"), + "password": os.Getenv("UNIFI_PASSWORD"), + } + transport := &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + } + jar, _ := cookiejar.New(nil) + client := &http.Client{ + Transport: transport, + Jar: jar, + } + json, _ := json.Marshal(auth) + params := bytes.NewReader(json) + req, err := http.NewRequest("POST", url, params) + if err != nil { + return nil, err + } + + req.Header.Add("Accept", "application/json") + resp, err := client.Do(req) + if err != nil { + return nil, err + } + + if resp.StatusCode != http.StatusOK { + return nil, errors.New("Not a successful request") + } + + return client, nil +} diff --git a/integrations/promunifi/script/server b/integrations/promunifi/script/server new file mode 100644 index 00000000..f7ddd9f3 --- /dev/null +++ b/integrations/promunifi/script/server @@ -0,0 +1,9 @@ +#!/bin/bash +set -e + +# Load the environment variables needed for testing +export $(cat .env | grep -v ^# | xargs) + +go clean +go build -o unifi +./unifi