Build heroku compatible app
This commit is contained in:
commit
c226f420b2
|
|
@ -0,0 +1 @@
|
|||
.env
|
||||
|
|
@ -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"
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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
|
||||
Loading…
Reference in New Issue