Merge pull request #1 from davidnewhall/dn2_features

Features.
This commit is contained in:
David Newhall II 2018-04-28 18:41:22 -07:00 committed by GitHub
commit 7bd61d0be0
16 changed files with 3975 additions and 144 deletions

View File

@ -3,3 +3,5 @@
/*.1.gz
/*.1
/vendor
.DS_Store
*~

View File

@ -1,12 +1,104 @@
# Unifi
Collect your Unifi Controller Client data and send it to an InfluxDB instance. Grafana dashboard included.
![image](https://raw.githubusercontent.com/davidnewhall/unifi/master/grafana-unifi-dashboard.png)
Collect your Unifi Controller Data and send it to an InfluxDB instance.
Grafana dashboards included.
## Installation
[See the Wiki!](https://github.com/davidnewhall/unifi-poller/wiki/Installation)
# Backstory
Okay, so here's the deal. I found a simple piece of code on github that
sorta did what I needed; we all know that story. I wanted more data, so
I added more data collection. I believe I've completely rewritten every
piece of original code, except the copyright/license file and that's fine
by me. I probably wouldn't have made it this far if
[Garrett](https://github.com/dewski/unifi) hadn't written the original
code I started with. Many props my man.
The original code pulled only the client data. This app now pulls data
for clients, access points, security gateways and switches. I currently
own two UAP-AC-PROs, one USG-3 and one US-24-250W. If your devices differ
this app may miss some data. I'm willing to help and make it better.
Open an [Issue](https://github.com/davidnewhall/unifi-poller/issues) and
we'll figure out how to get things working for you.
# What's this data good for?
I've been trying to get my UAP data into Grafana. Sure, google search that.
You'll find [this](https://community.ubnt.com/t5/UniFi-Wireless/Grafana-dashboard-for-UniFi-APs-now-available/td-p/1833532).
And that's all you'll find. What if you don't want to deal with SNMP?
Well, here you go. I've replicated 90% of what you see on those SNMP-powered
dashboards with this Go app running on the same mac as my Unifi controller.
All without enabling SNMP nor trying to understand those OIDs. Mad props
to [waterside](https://community.ubnt.com/t5/user/viewprofilepage/user-id/303058)
for making this dashboard; it gave me a fantastic start to making my own.
# What now...
- I probably suck at InfluxDB.
I don't know what should be a tag and what should be a field. I think
I did my best, but there's certainly room for improvements in both
the data input and the Grafana graphs (output).
- The USW and USG code needs love.
Up to this point, my focus has been on UAP. I have only included dashboards
that focus on UAP. I am still working on the other two, but it may be a while
before I get around to publishing them. Help is appreciated.
- Are there other devices that need to be included?
I have: switch, router, access point. Three total, and the type structs are
likely missing data for variants of these devices. e.g. Some UAPs have more
radios, I probably didn't properly account for that. Some gateways have more
ports, some switches have 10Gb, etc. These are things I do not have data on
to write code for. If you have these devices, and want them graphed, open an
Issue and lets discuss.
- Better Installation instructions.
If you're a nerd you can probably figure it out. I'd still like some pretty
pictures and maybe even a Twitch VOD.
- Sanity Checking
Did I actually graph the right data in the right way? Some validation would
be nice.
- Radios, Frequencies, Interfaces, vAPs
My access points only seem to have two radios, one interface and vAP per radio.
I'm not sure if the graphs, as-is, provide enough insight into APs with other
configurations. Help me figure that out?
# What's it look like?
Here's a picture of the Client dashboard.
![image](images/unifi-clients-dashboard.png?raw=true)
Here's a picture of the UAP dashboard. This only shows one device, but you can
select multiple to put specific stats side-by-side.
![image](images/unifi-uap-dashboard.png?raw=true)
# Woah, there's a library in here.
Sure is. If you want to write your own code around unifi data, you can import
the `unidev` library and easily poll your controller. If you have interest in
how to use the library, or need more features, open an Issue. I'm not committed
to documenting it unless it seems useful.
## Copyright & License
Copyright © 2016 Garrett Bjerkhoel. See [MIT-LICENSE](MIT-LICENSE) for details.
- Copyright © 2016 Garrett Bjerkhoel.
- Copyright © 2018 David Newhall II.
- See [MIT-LICENSE](MIT-LICENSE) for license information.

View File

@ -4,7 +4,7 @@ import "time"
// Version will be injected at build time.
var (
Version = "v0.1"
Version = "v0.2"
Debug = false
)

View File

@ -83,14 +83,14 @@ func GetConfig(configFile string) (Config, error) {
func (c *Config) PollUnifiController(infdb influx.Client, unifi *unidev.AuthedReq) {
ticker := time.NewTicker(c.Interval.value)
for range ticker.C {
clients, err := unifi.GetUnifiClients()
clients, err := unifi.GetUnifiClientAssets()
if err != nil {
log.Println("unifi.GetUnifiClients():", err)
log.Println("unifi.GetUnifiClientsAssets():", err)
continue
}
devices, err := unifi.GetUnifiDevices()
devices, err := unifi.GetUnifiDeviceAssets()
if err != nil {
log.Println("unifi.GetUnifiDevices():", err)
log.Println("unifi.GetUnifiDeviceAssets():", err)
continue
}
bp, err := influx.NewBatchPoints(influx.BatchPointsConfig{
@ -102,10 +102,10 @@ func (c *Config) PollUnifiController(infdb influx.Client, unifi *unidev.AuthedRe
}
for _, asset := range append(clients, devices...) {
if pt, errr := asset.Point(); errr != nil {
log.Println("asset.Point():", errr)
if pt, errr := asset.Points(); errr != nil {
log.Println("asset.Points():", errr)
} else {
bp.AddPoint(pt)
bp.AddPoints(pt)
}
}

View File

@ -0,0 +1,5 @@
# Grafana Dashboards
Import these into Grafana to quickly visualize data from your devices.
They may/do use a few plugins: Clock, Discrete, Singlestat, Table

File diff suppressed because it is too large Load Diff

View File

Before

Width:  |  Height:  |  Size: 246 KiB

After

Width:  |  Height:  |  Size: 246 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 141 KiB

View File

@ -7,8 +7,9 @@ import (
influx "github.com/influxdata/influxdb/client/v2"
)
// Point generates a client's datapoint for InfluxDB.
func (u UCL) Point() (*influx.Point, error) {
// Points generates a client's datapoints for InfluxDB.
func (u UCL) Points() ([]*influx.Point, error) {
var points []*influx.Point
if u.Name == "" && u.Hostname != "" {
u.Name = u.Hostname
} else if u.Hostname == "" && u.Name != "" {
@ -95,6 +96,9 @@ func (u UCL) Point() (*influx.Point, error) {
"wired-tx_bytes-r": u.WiredTxBytesR,
"wired-tx_packets": u.WiredTxPackets,
}
return influx.NewPoint("clients", tags, fields, time.Now())
pt, err := influx.NewPoint("clients", tags, fields, time.Now())
if err == nil {
points = append(points, pt)
}
return points, err
}

View File

@ -7,8 +7,12 @@ import (
influx "github.com/influxdata/influxdb/client/v2"
)
// Point generates a device's datapoint for InfluxDB.
func (u UAP) Point() (*influx.Point, error) {
// Points generates a device's datapoints for InfluxDB.
func (u UAP) Points() ([]*influx.Point, error) {
/* I generally suck at InfluxDB, so if I got the tags/fields wrong,
please send me a PR or open an Issue to address my faults. Thanks!
*/
var points []*influx.Point
tags := map[string]string{
"id": u.ID,
"mac": u.Mac,
@ -35,7 +39,7 @@ func (u UAP) Point() (*influx.Point, error) {
"has_speaker": strconv.FormatBool(u.HasSpeaker),
"inform_ip": u.InformIP,
"isolated": strconv.FormatBool(u.Isolated),
"last_seen": strconv.Itoa(u.LastSeen),
"last_seen": strconv.FormatFloat(u.LastSeen, 'f', 6, 64),
"last_uplink_mac": u.LastUplink.UplinkMac,
"last_uplink_remote_port": strconv.Itoa(u.LastUplink.UplinkRemotePort),
"known_cfgversion": u.KnownCfgversion,
@ -74,6 +78,7 @@ func (u UAP) Point() (*influx.Point, error) {
"loadavg_15": u.SysStats.Loadavg15,
"mem_buffer": u.SysStats.MemBuffer,
"mem_total": u.SysStats.MemTotal,
"mem_used": u.SysStats.MemUsed,
"cpu": u.SystemStats.CPU,
"mem": u.SystemStats.Mem,
"system_uptime": u.SystemStats.Uptime,
@ -165,5 +170,98 @@ func (u UAP) Point() (*influx.Point, error) {
"stat_wifi1-tx_packets": u.Stat.Wifi1TxPackets,
"stat_wifi1-tx_retries": u.Stat.Wifi1TxRetries,
}
return influx.NewPoint("uap", tags, fields, time.Now())
pt, err := influx.NewPoint("uap", tags, fields, time.Now())
if err != nil {
return nil, err
}
points = append(points, pt)
for _, p := range u.RadioTable {
tags := map[string]string{
"device_name": u.Name,
"device_id": u.ID,
"device_mac": u.Mac,
"name": p.Name,
"wlangroup_id": p.WlangroupID,
"channel": p.Channel, // not the channel #
"radio": p.Radio,
}
fields := map[string]interface{}{
"builtin_ant_gain": p.BuiltinAntGain,
"current_antenna_gain": p.CurrentAntennaGain,
"has_dfs": p.HasDfs,
"has_fccdfs": p.HasFccdfs,
"ht": p.Ht,
"is_11ac": p.Is11Ac,
"max_txpower": p.MaxTxpower,
"min_rssi_enabled": p.MinRssiEnabled,
"min_txpower": p.MinTxpower,
"nss": p.Nss,
"radio_caps": p.RadioCaps,
"tx_power": p.TxPower,
"tx_power_mode": p.TxPowerMode,
}
for _, s := range u.RadioTableStats {
// This may be a tad slower but it allows putting
// all the radio stats into one table.
if p.Name == s.Name {
fields["ast_be_xmit"] = s.AstBeXmit
fields["ast_cst"] = s.AstCst
fields["channel"] = s.Channel
fields["ast_txto"] = s.AstTxto
fields["cu_self_rx"] = s.CuSelfRx
fields["cu_self_tx"] = s.CuSelfTx
fields["cu_total"] = s.CuTotal
fields["extchannel"] = s.Extchannel
fields["gain"] = s.Gain
fields["guest-num_sta"] = s.GuestNumSta
fields["num_sta"] = s.NumSta
fields["radio"] = s.Radio
fields["state"] = s.State
fields["radio_tx_packets"] = s.TxPackets
fields["radio_tx_power"] = s.TxPower
fields["radio_tx_retries"] = s.TxRetries
fields["user-num_sta"] = s.UserNumSta
}
}
for _, s := range u.VapTable {
if p.Name == s.RadioName {
tags["ap_mac"] = s.ApMac
tags["bssid"] = s.Bssid
fields["ccq"] = s.Ccq
fields["essid"] = s.Essid
fields["extchannel"] = s.Extchannel
tags["vap_id"] = s.ID
fields["is_guest"] = s.IsGuest
fields["is_wep"] = s.IsWep
fields["mac_filter_rejections"] = s.MacFilterRejections
fields["map_id"] = s.MapID
tags["vap_name"] = s.Name
fields["rx_bytes"] = s.RxBytes
fields["rx_crypts"] = s.RxCrypts
fields["rx_dropped"] = s.RxDropped
fields["rx_errors"] = s.RxErrors
fields["rx_frags"] = s.RxFrags
fields["rx_nwids"] = s.RxNwids
fields["rx_packets"] = s.RxPackets
fields["tx_bytes"] = s.TxBytes
fields["tx_dropped"] = s.TxDropped
fields["tx_errors"] = s.TxErrors
fields["tx_latency_avg"] = s.TxLatencyAvg
fields["tx_latency_max"] = s.TxLatencyMax
fields["tx_latency_min"] = s.TxLatencyMin
fields["tx_packets"] = s.TxPackets
fields["tx_power"] = s.TxPower
fields["tx_retries"] = s.TxRetries
fields["usage"] = s.Usage
tags["wlanconf_id"] = s.WlanconfID
}
}
pt, err := influx.NewPoint("uap_radios", tags, fields, time.Now())
if err != nil {
return points, err
}
points = append(points, pt)
}
return points, nil
}

View File

@ -1,7 +1,12 @@
package unidev
// UAP is a Unifi Access Point
// UAP is a Unifi Access Point.
type UAP struct {
/* This was auto generated and then slowly edited by hand
to get all the data types right and graphable.
No ones feelings will be hurt if you want to break this
up into multiple structs, and/or make it better in general.
*/
ID string `json:"_id"`
UUptime float64 `json:"_uptime"`
AdoptIP string `json:"adopt_ip,omitempty"`
@ -36,17 +41,17 @@ type UAP struct {
Name string `json:"name"`
NumPort float64 `json:"num_port"`
} `json:"ethernet_table"`
FwCaps int `json:"fw_caps"`
GuestNumSta int `json:"guest-num_sta"`
GuestToken string `json:"guest_token"`
HasEth1 bool `json:"has_eth1"`
HasSpeaker bool `json:"has_speaker"`
InformIP string `json:"inform_ip"`
InformURL string `json:"inform_url"`
IP string `json:"ip"`
Isolated bool `json:"isolated"`
KnownCfgversion string `json:"known_cfgversion"`
LastSeen int `json:"last_seen"`
FwCaps int `json:"fw_caps"`
GuestNumSta int `json:"guest-num_sta"`
GuestToken string `json:"guest_token"`
HasEth1 bool `json:"has_eth1"`
HasSpeaker bool `json:"has_speaker"`
InformIP string `json:"inform_ip"`
InformURL string `json:"inform_url"`
IP string `json:"ip"`
Isolated bool `json:"isolated"`
KnownCfgversion string `json:"known_cfgversion"`
LastSeen float64 `json:"last_seen"`
LastUplink struct {
UplinkMac string `json:"uplink_mac"`
UplinkRemotePort int `json:"uplink_remote_port"`
@ -132,28 +137,28 @@ type UAP struct {
Is11Ac bool `json:"is_11ac,omitempty"`
} `json:"radio_table"`
RadioTableStats []struct {
AstBeXmit interface{} `json:"ast_be_xmit"`
AstCst interface{} `json:"ast_cst"`
AstTxto interface{} `json:"ast_txto"`
Channel float64 `json:"channel"`
CuSelfRx float64 `json:"cu_self_rx"`
CuSelfTx float64 `json:"cu_self_tx"`
CuTotal float64 `json:"cu_total"`
Extchannel float64 `json:"extchannel"`
Gain float64 `json:"gain"`
GuestNumSta float64 `json:"guest-num_sta"`
Name string `json:"name"`
NumSta float64 `json:"num_sta"`
Radio string `json:"radio"`
State string `json:"state"`
TxPackets float64 `json:"tx_packets"`
TxPower float64 `json:"tx_power"`
TxRetries float64 `json:"tx_retries"`
UserNumSta float64 `json:"user-num_sta"`
AstBeXmit float64 `json:"ast_be_xmit"`
AstCst float64 `json:"ast_cst"`
AstTxto float64 `json:"ast_txto"`
Channel float64 `json:"channel"`
CuSelfRx float64 `json:"cu_self_rx"`
CuSelfTx float64 `json:"cu_self_tx"`
CuTotal float64 `json:"cu_total"`
Extchannel float64 `json:"extchannel"`
Gain float64 `json:"gain"`
GuestNumSta float64 `json:"guest-num_sta"`
Name string `json:"name"`
NumSta float64 `json:"num_sta"`
Radio string `json:"radio"`
State string `json:"state"`
TxPackets float64 `json:"tx_packets"`
TxPower float64 `json:"tx_power"`
TxRetries float64 `json:"tx_retries"`
UserNumSta float64 `json:"user-num_sta"`
} `json:"radio_table_stats"`
Rollupgrade bool `json:"rollupgrade"`
RxBytes int `json:"rx_bytes"`
RxBytesD int `json:"rx_bytes-d"`
RxBytes float64 `json:"rx_bytes"`
RxBytesD float64 `json:"rx_bytes-d"`
ScanRadioTable []interface{} `json:"scan_radio_table"`
Scanning bool `json:"scanning"`
Serial string `json:"serial"`
@ -257,20 +262,20 @@ type UAP struct {
} `json:"stat"`
State int `json:"state"`
SysStats struct {
Loadavg1 string `json:"loadavg_1"`
Loadavg15 string `json:"loadavg_15"`
Loadavg5 string `json:"loadavg_5"`
MemBuffer int `json:"mem_buffer"`
MemTotal int `json:"mem_total"`
MemUsed int `json:"mem_used"`
Loadavg1 float64 `json:"loadavg_1,string"`
Loadavg15 float64 `json:"loadavg_15,string"`
Loadavg5 float64 `json:"loadavg_5,string"`
MemBuffer float64 `json:"mem_buffer"`
MemTotal float64 `json:"mem_total"`
MemUsed float64 `json:"mem_used"`
} `json:"sys_stats"`
SystemStats struct {
CPU string `json:"cpu"`
Mem string `json:"mem"`
Uptime string `json:"uptime"`
CPU float64 `json:"cpu,string"`
Mem float64 `json:"mem,string"`
Uptime float64 `json:"uptime,string"`
} `json:"system-stats"`
TxBytes float64 `json:"tx_bytes"`
TxBytesD int `json:"tx_bytes-d"`
TxBytesD float64 `json:"tx_bytes-d"`
Type string `json:"type"`
Upgradable bool `json:"upgradable"`
Uplink struct {
@ -284,63 +289,63 @@ type UAP struct {
Netmask string `json:"netmask"`
NumPort int `json:"num_port"`
RxBytes float64 `json:"rx_bytes"`
RxBytesR int `json:"rx_bytes-r"`
RxDropped int `json:"rx_dropped"`
RxErrors int `json:"rx_errors"`
RxMulticast int `json:"rx_multicast"`
RxPackets int `json:"rx_packets"`
Speed int `json:"speed"`
RxBytesR float64 `json:"rx_bytes-r"`
RxDropped float64 `json:"rx_dropped"`
RxErrors float64 `json:"rx_errors"`
RxMulticast float64 `json:"rx_multicast"`
RxPackets float64 `json:"rx_packets"`
Speed float64 `json:"speed"`
TxBytes float64 `json:"tx_bytes"`
TxBytesR int `json:"tx_bytes-r"`
TxDropped int `json:"tx_dropped"`
TxErrors int `json:"tx_errors"`
TxPackets int `json:"tx_packets"`
TxBytesR float64 `json:"tx_bytes-r"`
TxDropped float64 `json:"tx_dropped"`
TxErrors float64 `json:"tx_errors"`
TxPackets float64 `json:"tx_packets"`
Type string `json:"type"`
Up bool `json:"up"`
UplinkMac string `json:"uplink_mac"`
UplinkRemotePort int `json:"uplink_remote_port"`
} `json:"uplink"`
UplinkTable []interface{} `json:"uplink_table"`
Uptime int `json:"uptime"`
Uptime float64 `json:"uptime"`
UserNumSta int `json:"user-num_sta"`
VapTable []struct {
ApMac string `json:"ap_mac"`
Bssid string `json:"bssid"`
Ccq int `json:"ccq"`
Channel int `json:"channel"`
Essid string `json:"essid"`
Extchannel int `json:"extchannel"`
ID string `json:"id"`
IsGuest bool `json:"is_guest"`
IsWep bool `json:"is_wep"`
MacFilterRejections int `json:"mac_filter_rejections"`
MapID interface{} `json:"map_id"`
Name string `json:"name"`
NumSta int `json:"num_sta"`
Radio string `json:"radio"`
RadioName string `json:"radio_name"`
RxBytes int `json:"rx_bytes"`
RxCrypts int `json:"rx_crypts"`
RxDropped int `json:"rx_dropped"`
RxErrors int `json:"rx_errors"`
RxFrags int `json:"rx_frags"`
RxNwids int `json:"rx_nwids"`
RxPackets int `json:"rx_packets"`
SiteID string `json:"site_id"`
State string `json:"state"`
T string `json:"t"`
TxBytes int `json:"tx_bytes"`
TxDropped int `json:"tx_dropped"`
TxErrors int `json:"tx_errors"`
TxLatencyAvg float64 `json:"tx_latency_avg"`
TxLatencyMax float64 `json:"tx_latency_max"`
TxLatencyMin float64 `json:"tx_latency_min"`
TxPackets int `json:"tx_packets"`
TxPower int `json:"tx_power"`
TxRetries int `json:"tx_retries"`
Up bool `json:"up"`
Usage string `json:"usage"`
WlanconfID string `json:"wlanconf_id"`
ApMac string `json:"ap_mac"`
Bssid string `json:"bssid"`
Ccq int `json:"ccq"`
Channel int `json:"channel"`
Essid string `json:"essid"`
Extchannel int `json:"extchannel"`
ID string `json:"id"`
IsGuest bool `json:"is_guest"`
IsWep bool `json:"is_wep"`
MacFilterRejections int `json:"mac_filter_rejections"`
MapID string `json:"map_id"`
Name string `json:"name"`
NumSta int `json:"num_sta"`
Radio string `json:"radio"`
RadioName string `json:"radio_name"`
RxBytes float64 `json:"rx_bytes"`
RxCrypts float64 `json:"rx_crypts"`
RxDropped float64 `json:"rx_dropped"`
RxErrors float64 `json:"rx_errors"`
RxFrags float64 `json:"rx_frags"`
RxNwids float64 `json:"rx_nwids"`
RxPackets float64 `json:"rx_packets"`
SiteID string `json:"site_id"`
State string `json:"state"`
T string `json:"t"`
TxBytes float64 `json:"tx_bytes"`
TxDropped float64 `json:"tx_dropped"`
TxErrors float64 `json:"tx_errors"`
TxLatencyAvg float64 `json:"tx_latency_avg"`
TxLatencyMax float64 `json:"tx_latency_max"`
TxLatencyMin float64 `json:"tx_latency_min"`
TxPackets float64 `json:"tx_packets"`
TxPower int `json:"tx_power"`
TxRetries int `json:"tx_retries"`
Up bool `json:"up"`
Usage string `json:"usage"`
WlanconfID string `json:"wlanconf_id"`
} `json:"vap_table"`
Version string `json:"version"`
VersionIncompatible bool `json:"version_incompatible"`

View File

@ -13,9 +13,12 @@ import (
const LoginPath = "/api/login"
// Asset provides a common interface to retreive metrics from a device or client.
// It currently only supports InfluxDB, but could be amended to support other
// libraries that have a similar interface.
// This app only uses the .AddPoint/s() methods with the Asset type.
type Asset interface {
// Point() means this is useful to influxdb..
Point() (*influx.Point, error)
Points() ([]*influx.Point, error)
// Add more methods to achieve more usefulness from this library.
}

View File

@ -21,7 +21,7 @@ const (
)
// GetUnifiClients returns a response full of clients' data from the Unifi Controller.
func (c *AuthedReq) GetUnifiClients() ([]Asset, error) {
func (c *AuthedReq) GetUnifiClients() ([]UCL, error) {
var response struct {
Clients []UCL `json:"data"`
Meta struct {
@ -34,68 +34,103 @@ func (c *AuthedReq) GetUnifiClients() ([]Asset, error) {
return nil, err
} else if body, err := ioutil.ReadAll(resp.Body); err != nil {
return nil, err
} else if err = json.Unmarshal(body, response); err != nil {
} else if err = json.Unmarshal(body, &response); err != nil {
return nil, err
} else if err = resp.Body.Close(); err != nil {
log.Println("resp.Body.Close():", err) // Not fatal? Just log it.
}
clients := []Asset{}
for _, r := range response.Clients {
clients = append(clients, r)
return response.Clients, nil
}
// GetUnifiClientAssets provides an interface to return common asset types.
func (c *AuthedReq) GetUnifiClientAssets() ([]Asset, error) {
clients, err := c.GetUnifiClients()
assets := []Asset{}
if err == nil {
for _, r := range clients {
assets = append(assets, r)
}
}
return clients, nil
return assets, err
}
// GetUnifiDevices returns a response full of devices' data from the Unifi Controller.
func (c *AuthedReq) GetUnifiDevices() ([]Asset, error) {
func (c *AuthedReq) GetUnifiDevices() ([]USG, []USW, []UAP, error) {
var parsed struct {
Data []json.RawMessage `json:"data"`
Meta struct {
Rc string `json:"rc"`
} `json:"meta"`
}
assets := []Asset{}
if req, err := c.UniReq(DevicePath, ""); err != nil {
return nil, err
return nil, nil, nil, err
} else if resp, err := c.Do(req); err != nil {
return nil, err
return nil, nil, nil, err
} else if body, err := ioutil.ReadAll(resp.Body); err != nil {
return nil, err
return nil, nil, nil, err
} else if err = json.Unmarshal(body, &parsed); err != nil {
return nil, err
return nil, nil, nil, err
} else if err = resp.Body.Close(); err != nil {
log.Println("resp.Body.Close():", err) // Not fatal? Just log it.
}
var usgs []USG
var usws []USW
var uaps []UAP
for _, r := range parsed.Data {
var usg USG
var usw USW
var uap UAP
// Unamrshal into a map and check "type"
var obj map[string]interface{}
if err := json.Unmarshal(r, &obj); err != nil {
return nil, err
return nil, nil, nil, err
}
assetType := ""
assetType := "- missing -"
if t, ok := obj["type"].(string); ok {
assetType = t
}
if Debug {
log.Println("Unmarshalling Device Type:", assetType)
}
// Unmarshal again into the correct type..
var asset Asset
switch assetType {
case "uap":
asset = &UAP{}
case "ugw":
asset = &USG{}
if err := json.Unmarshal(r, &uap); err != nil {
return nil, nil, nil, err
}
uaps = append(uaps, uap)
case "ugw", "usg": // in case they ever fix the name in the api.
if err := json.Unmarshal(r, &usg); err != nil {
return nil, nil, nil, err
}
usgs = append(usgs, usg)
case "usw":
asset = &USW{}
if err := json.Unmarshal(r, &usw); err != nil {
return nil, nil, nil, err
}
usws = append(usws, usw)
default:
log.Println("unknown asset type -", assetType, "- skipping")
continue
}
if Debug {
log.Println("Unmarshalling", assetType)
}
if err := json.Unmarshal(r, asset); err != nil {
return nil, err
}
assets = append(assets, asset)
}
return assets, nil
return usgs, usws, uaps, nil
}
// GetUnifiDeviceAssets provides an interface to return common asset types.
func (c *AuthedReq) GetUnifiDeviceAssets() ([]Asset, error) {
usgs, usws, uaps, err := c.GetUnifiDevices()
assets := []Asset{}
if err == nil {
for _, r := range usgs {
assets = append(assets, r)
}
for _, r := range usws {
assets = append(assets, r)
}
for _, r := range uaps {
assets = append(assets, r)
}
}
return assets, err
}

View File

@ -7,8 +7,9 @@ import (
influx "github.com/influxdata/influxdb/client/v2"
)
// Point generates a device's datapoint for InfluxDB.
func (u USG) Point() (*influx.Point, error) {
// Points generates a device's datapoints for InfluxDB.
func (u USG) Points() ([]*influx.Point, error) {
var points []*influx.Point
tags := map[string]string{
"id": u.ID,
"mac": u.Mac,
@ -117,5 +118,9 @@ func (u USG) Point() (*influx.Point, error) {
"wan-tx_bytes": u.Stat.WanTxBytes,
"wan-tx_packets": u.Stat.WanTxPackets,
}
return influx.NewPoint("usg", tags, fields, time.Now())
pt, err := influx.NewPoint("usg", tags, fields, time.Now())
if err == nil {
points = append(points, pt)
}
return points, err
}

View File

@ -7,8 +7,9 @@ import (
influx "github.com/influxdata/influxdb/client/v2"
)
// Point generates a device's datapoint for InfluxDB.
func (u USW) Point() (*influx.Point, error) {
// Points generates a device's datapoints for InfluxDB.
func (u USW) Points() ([]*influx.Point, error) {
var points []*influx.Point
tags := map[string]string{
"id": u.ID,
"mac": u.Mac,
@ -109,5 +110,9 @@ func (u USW) Point() (*influx.Point, error) {
"stat_tx_retries": u.Stat.TxRetries,
// Add the port stats too.
}
return influx.NewPoint("usw", tags, fields, time.Now())
pt, err := influx.NewPoint("usw", tags, fields, time.Now())
if err == nil {
points = append(points, pt)
}
return points, err
}