add v4 folder

This commit is contained in:
davidnewhall2 2019-12-27 20:56:27 -08:00
parent 92b79d50c5
commit 115ee799a3
25 changed files with 7892 additions and 0 deletions

23
core/unifi/v4/LICENSE Normal file
View File

@ -0,0 +1,23 @@
MIT License
Copyright (c) 2019 Go Lift - Building Strong Go Tools
Copyright (c) 2018 David Newhall II
Copyright (c) 2016 Garrett Bjerkhoel
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

64
core/unifi/v4/README.md Normal file
View File

@ -0,0 +1,64 @@
# Go Library: `unifi`
It connects to a Unifi Controller, given a url, username and password. Returns
an authenticated http Client you may use to query the device for data. Also
contains some built-in methods for de-serializing common client and device
data. The data is provided in a large struct you can consume in your application.
This library is designed to PULL data FROM the controller. It has no methods that
update settings or change things on the controller.
[Someone expressed interest](https://github.com/golift/unifi/issues/31) in
adding methods to update data, and I'm okay with that. I'll even help add them.
[Tell me what you want to do](https://github.com/golift/unifi/issues/new), and we'll make it happen.
Pull requests, feature requests, code reviews and feedback are welcomed!
Here's a working example:
```golang
package main
import "log"
import "golift.io/unifi"
func main() {
c := *unifi.Config{
User: "admin",
Pass: "superSecret1234",
URL: "https://127.0.0.1:8443/",
// Log with log.Printf or make your own interface that accepts (msg, fmt)
ErrorLog: log.Printf,
DebugLog: log.Printf,
}
uni, err := unifi.NewUnifi(c)
if err != nil {
log.Fatalln("Error:", err)
}
sites, err := uni.GetSites()
if err != nil {
log.Fatalln("Error:", err)
}
clients, err := uni.GetClients(sites)
if err != nil {
log.Fatalln("Error:", err)
}
devices, err := uni.GetDevices(sites)
if err != nil {
log.Fatalln("Error:", err)
}
log.Println(len(sites), "Unifi Sites Found: ", sites)
log.Println(len(clients), "Clients connected:")
for i, client := range clients {
log.Println(i+1, client.ID, client.Hostname, client.IP, client.Name, client.LastSeen)
}
log.Println(len(devices.USWs), "Unifi Switches Found")
log.Println(len(devices.USGs), "Unifi Gateways Found")
log.Println(len(devices.UAPs), "Unifi Wireless APs Found:")
for i, uap := range devices.UAPs {
log.Println(i+1, uap.Name, uap.IP)
}
}
```

156
core/unifi/v4/clients.go Normal file
View File

@ -0,0 +1,156 @@
package unifi
import (
"fmt"
)
// GetClients returns a response full of clients' data from the UniFi Controller.
func (u *Unifi) GetClients(sites Sites) (Clients, error) {
data := make([]*Client, 0)
for _, site := range sites {
var response struct {
Data []*Client `json:"data"`
}
u.DebugLog("Polling Controller, retreiving UniFi Clients, site %s (%s) ", site.Name, site.Desc)
clientPath := fmt.Sprintf(APIClientPath, site.Name)
if err := u.GetData(clientPath, &response); err != nil {
return nil, err
}
for i, d := range response.Data {
// Add special SourceName value.
response.Data[i].SourceName = u.URL
// Add the special "Site Name" to each client. This becomes a Grafana filter somewhere.
response.Data[i].SiteName = site.Desc + " (" + site.Name + ")"
// Fix name and hostname fields. Sometimes one or the other is blank.
response.Data[i].Hostname = pick(d.Hostname, d.Name, d.Mac)
response.Data[i].Name = pick(d.Name, d.Hostname)
}
data = append(data, response.Data...)
}
return data, nil
}
// GetClientsDPI garners dpi data for clients.
func (u *Unifi) GetClientsDPI(sites Sites) ([]*DPITable, error) {
var data []*DPITable
for _, site := range sites {
u.DebugLog("Polling Controller, retreiving Client DPI data, site %s (%s) ", site.Name, site.Desc)
var response struct {
Data []*DPITable `json:"data"`
}
clientDPIpath := fmt.Sprintf(APIClientDPI, site.Name)
if err := u.GetData(clientDPIpath, &response, `{"type":"by_app"}`); err != nil {
return nil, err
}
for _, d := range response.Data {
d.SourceName = site.SourceName
d.SiteName = site.SiteName
data = append(data, d)
}
}
return data, nil
}
// Clients contains a list that contains all of the unifi clients from a controller.
type Clients []*Client
// Client defines all the data a connected-network client contains.
type Client struct {
SourceName string `json:"-"`
Anomalies int64 `json:"anomalies,omitempty"`
ApMac string `json:"ap_mac"`
ApName string `json:"-"`
AssocTime int64 `json:"assoc_time"`
Blocked bool `json:"blocked,omitempty"`
Bssid string `json:"bssid"`
BytesR int64 `json:"bytes-r"`
Ccq int64 `json:"ccq"`
Channel FlexInt `json:"channel"`
DevCat FlexInt `json:"dev_cat"`
DevFamily FlexInt `json:"dev_family"`
DevID FlexInt `json:"dev_id"`
DevVendor FlexInt `json:"dev_vendor,omitempty"`
DhcpendTime int `json:"dhcpend_time,omitempty"`
Satisfaction FlexInt `json:"satisfaction,omitempty"`
Essid string `json:"essid"`
FirstSeen int64 `json:"first_seen"`
FixedIP string `json:"fixed_ip"`
GwMac string `json:"gw_mac"`
GwName string `json:"-"`
Hostname string `json:"hostname"`
ID string `json:"_id"`
IP string `json:"ip"`
IdleTime int64 `json:"idle_time"`
Is11R FlexBool `json:"is_11r"`
IsGuest FlexBool `json:"is_guest"`
IsGuestByUAP FlexBool `json:"_is_guest_by_uap"`
IsGuestByUGW FlexBool `json:"_is_guest_by_ugw"`
IsGuestByUSW FlexBool `json:"_is_guest_by_usw"`
IsWired FlexBool `json:"is_wired"`
LastSeen int64 `json:"last_seen"`
LastSeenByUAP int64 `json:"_last_seen_by_uap"`
LastSeenByUGW int64 `json:"_last_seen_by_ugw"`
LastSeenByUSW int64 `json:"_last_seen_by_usw"`
LatestAssocTime int64 `json:"latest_assoc_time"`
Mac string `json:"mac"`
Name string `json:"name"`
Network string `json:"network"`
NetworkID string `json:"network_id"`
Noise int64 `json:"noise"`
Note string `json:"note"`
Noted FlexBool `json:"noted"`
OsClass FlexInt `json:"os_class"`
OsName FlexInt `json:"os_name"`
Oui string `json:"oui"`
PowersaveEnabled FlexBool `json:"powersave_enabled"`
QosPolicyApplied FlexBool `json:"qos_policy_applied"`
Radio string `json:"radio"`
RadioName string `json:"radio_name"`
RadioProto string `json:"radio_proto"`
RadioDescription string `json:"-"`
RoamCount int64 `json:"roam_count"`
Rssi int64 `json:"rssi"`
RxBytes int64 `json:"rx_bytes"`
RxBytesR int64 `json:"rx_bytes-r"`
RxPackets int64 `json:"rx_packets"`
RxRate int64 `json:"rx_rate"`
Signal int64 `json:"signal"`
SiteID string `json:"site_id"`
SiteName string `json:"-"`
SwDepth int `json:"sw_depth"`
SwMac string `json:"sw_mac"`
SwName string `json:"-"`
SwPort FlexInt `json:"sw_port"`
TxBytes int64 `json:"tx_bytes"`
TxBytesR int64 `json:"tx_bytes-r"`
TxPackets int64 `json:"tx_packets"`
TxRetries int64 `json:"tx_retries"`
TxPower int64 `json:"tx_power"`
TxRate int64 `json:"tx_rate"`
Uptime int64 `json:"uptime"`
UptimeByUAP int64 `json:"_uptime_by_uap"`
UptimeByUGW int64 `json:"_uptime_by_ugw"`
UptimeByUSW int64 `json:"_uptime_by_usw"`
UseFixedIP FlexBool `json:"use_fixedip"`
UserGroupID string `json:"usergroup_id"`
UserID string `json:"user_id"`
Vlan FlexInt `json:"vlan"`
WifiTxAttempts int64 `json:"wifi_tx_attempts"`
WiredRxBytes int64 `json:"wired-rx_bytes"`
WiredRxBytesR int64 `json:"wired-rx_bytes-r"`
WiredRxPackets int64 `json:"wired-rx_packets"`
WiredTxBytes int64 `json:"wired-tx_bytes"`
WiredTxBytesR int64 `json:"wired-tx_bytes-r"`
WiredTxPackets int64 `json:"wired-tx_packets"`
}

107
core/unifi/v4/devices.go Normal file
View File

@ -0,0 +1,107 @@
package unifi
import (
"encoding/json"
"fmt"
)
// GetDevices returns a response full of devices' data from the UniFi Controller.
func (u *Unifi) GetDevices(sites Sites) (*Devices, error) {
devices := new(Devices)
for _, site := range sites {
var response struct {
Data []json.RawMessage `json:"data"`
}
devicePath := fmt.Sprintf(APIDevicePath, site.Name)
if err := u.GetData(devicePath, &response); err != nil {
return nil, err
}
loopDevices := u.parseDevices(response.Data, site.SiteName)
devices.UAPs = append(devices.UAPs, loopDevices.UAPs...)
devices.USGs = append(devices.USGs, loopDevices.USGs...)
devices.USWs = append(devices.USWs, loopDevices.USWs...)
devices.UDMs = append(devices.UDMs, loopDevices.UDMs...)
}
return devices, nil
}
// parseDevices parses the raw JSON from the Unifi Controller into device structures.
func (u *Unifi) parseDevices(data []json.RawMessage, siteName string) *Devices {
devices := new(Devices)
for _, r := range data {
// Loop each item in the raw JSON message, detect its type and unmarshal it.
assetType := "<type key missing>"
if o := make(map[string]interface{}); u.unmarshalDevice("map", r, &o) != nil {
continue
} else if t, ok := o["type"].(string); ok {
assetType = t
}
u.DebugLog("Unmarshalling Device Type: %v, site %s ", assetType, siteName)
// Choose which type to unmarshal into based on the "type" json key.
switch assetType { // Unmarshal again into the correct type..
case "uap":
dev := &UAP{SiteName: siteName, SourceName: u.URL}
if u.unmarshalDevice(assetType, r, dev) == nil {
dev.Name = pick(dev.Name, dev.Mac)
devices.UAPs = append(devices.UAPs, dev)
}
case "ugw", "usg": // in case they ever fix the name in the api.
dev := &USG{SiteName: siteName, SourceName: u.URL}
if u.unmarshalDevice(assetType, r, dev) == nil {
dev.Name = pick(dev.Name, dev.Mac)
devices.USGs = append(devices.USGs, dev)
}
case "usw":
dev := &USW{SiteName: siteName, SourceName: u.URL}
if u.unmarshalDevice(assetType, r, dev) == nil {
dev.Name = pick(dev.Name, dev.Mac)
devices.USWs = append(devices.USWs, dev)
}
case "udm":
dev := &UDM{SiteName: siteName, SourceName: u.URL}
if u.unmarshalDevice(assetType, r, dev) == nil {
dev.Name = pick(dev.Name, dev.Mac)
devices.UDMs = append(devices.UDMs, dev)
}
default:
u.ErrorLog("unknown asset type - %v - skipping", assetType)
}
}
return devices
}
// unmarshalDevice handles logging for the unmarshal operations in parseDevices().
func (u *Unifi) unmarshalDevice(dev string, data json.RawMessage, v interface{}) (err error) {
if err = json.Unmarshal(data, v); err != nil {
u.ErrorLog("json.Unmarshal(%v): %v", dev, err)
u.ErrorLog("Enable Debug Logging to output the failed payload.")
json, err := data.MarshalJSON()
u.DebugLog("Failed Payload: %s (marshal err: %v)", json, err)
u.DebugLog("The above payload can prove useful during torubleshooting when you open an Issue:")
u.DebugLog("==- https://github.com/golift/unifi/issues/new -==")
}
return err
}
// pick returns the first non empty string in a list.
// used in a few places around this library.
func pick(strings ...string) string {
for _, s := range strings {
if s != "" {
return s
}
}
return ""
}

2366
core/unifi/v4/dpi.go Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,26 @@
#!/bin/bash
# Usage:
# ./convert.sh <file> [prefix]
# <file> should contain a go struct, like uap_type.go
# It converts the go struct to an influx thing, like you see in uap_influx.go.
# [prefix] is optional. I used it to do all the stat_ uap metrics.
# Very crude, just helps skip a lot of copy/paste.
#
path=$1
pre=$2
# Reads in the file one line at a time.
while IFS='' read -r line; do
# Split each piece of the file out.
name=$(echo "${line}" | awk '{print $1}')
type=$(echo "${line}" | awk '{print $2}')
json=$(echo "${line}" | awk '{print $3}')
json=$(echo "${json}" | cut -d\" -f2)
# Don't print junk lines. (it still prints some junk lines)
if [ "$json" != "" ]; then
# Add a .Val suffix if this is a FlexInt or FlexBool.
[[ "$type" = Flex* ]] && suf=.Val
echo "\"${pre}${json}\": u.Stat.${name}${suf},"
fi
done < ${path}

View File

@ -0,0 +1,66 @@
{
"_id": "5d2416c78f0385ccf1c6df44",
"archived": false,
"timestamp": 1562646211,
"flow_id": 1591464006222389,
"in_iface": "eth1",
"event_type": "alert",
"src_ip": "196.196.244.84",
"src_mac": "f0:9f:c2:c4:bb:f1",
"src_port": 51413,
"dest_ip": "192.168.3.2",
"dst_mac": "40:a8:f0:68:c3:58",
"dest_port": 36881,
"proto": "UDP",
"app_proto": "failed",
"host": "f0:22:22:22:22:22",
"usgip": "11.22.33.44",
"unique_alertid": "1341902566-2019-07-08T21:23:31.229941-0700",
"srcipCountry": "SE",
"dstipCountry": false,
"usgipCountry": "US",
"srcipGeo": {
"continent_code": "EU",
"country_code": "SE",
"country_code3": "SWE",
"country_name": "Sweden",
"region": "26",
"city": "Stockholm",
"postal_code": "168 65",
"latitude": 59.349998474121094,
"longitude": 17.91670036315918,
"dma_code": 0,
"area_code": 0
},
"dstipGeo": false,
"usgipGeo": {
"continent_code": "NA",
"country_code": "US",
"country_code3": "USA",
"country_name": "United States",
"region": "CA",
"city": "Other",
"postal_code": "99999",
"latitude": 99.139400482177734,
"longitude": -99.39669799804688,
"dma_code": 862,
"area_code": 999
},
"srcipASN": "AS42607 Internet Carrier Limited",
"dstipASN": "",
"usgipASN": "AS7922 Comcast Cable Communications, LLC",
"catname": "spamhaus",
"inner_alert_action": "allowed",
"inner_alert_gid": 1,
"inner_alert_signature_id": 2400022,
"inner_alert_rev": 2673,
"inner_alert_signature": "ET DROP Spamhaus DROP Listed Traffic Inbound group 23",
"inner_alert_category": "Misc Attack",
"inner_alert_severity": 2,
"key": "EVT_IPS_IpsAlert",
"subsystem": "www",
"site_id": "574e86994566ffb914a2683c",
"time": 1562646211000,
"datetime": "2019-07-09T04:23:31Z",
"msg": "IPS Alert 2: Misc Attack. Signature ET DROP Spamhaus DROP Listed Traffic Inbound group 23. From: 196.196.244.84:51413, to: 192.168.3.2:36881, protocol: UDP"
},

View File

@ -0,0 +1,738 @@
{
"_id": "574e8bde4566ffb914a26853",
"adopted": true,
"antenna_table": [
{
"default": true,
"id": 4,
"name": "Combined",
"wifi0_gain": 3,
"wifi1_gain": 3
}
],
"bandsteering_mode": "prefer_5g",
"board_rev": 18,
"cfgversion": "08cd8d6b71ebe82f",
"config_network": {
"type": "dhcp",
"ip": "10.1.10.67"
},
"countrycode_table": [
840,
124,
630
],
"ethernet_table": [
{
"mac": "80:22:a8:22:ae:22",
"num_port": 2,
"name": "eth0"
}
],
"fw_caps": 4128063,
"has_eth1": false,
"has_speaker": false,
"inform_ip": "192.168.3.1",
"inform_url": "http://unifi:8080/inform",
"ip": "192.168.1.8",
"led_override": "default",
"mac": "80:22:22:22:22:22",
"mesh_sta_vap_enabled": true,
"model": "U7PG2",
"name": "wap-lower",
"outdoor_mode_override": "default",
"port_table": [
{
"port_idx": 1,
"op_mode": "switch",
"portconf_id": "574e869d4566ffb914a26845",
"attr_no_edit": true,
"media": "GE",
"name": "Main",
"poe_caps": 0,
"port_poe": false,
"tx_bytes-r": 0,
"rx_bytes-r": 0,
"bytes-r": 0,
"port_delta": {
"time_delta": 21
},
"enable": true,
"masked": false,
"aggregated_by": false
},
{
"port_idx": 2,
"op_mode": "switch",
"portconf_id": "574e869d4566ffb914a26845",
"media": "GE",
"name": "Secondary",
"poe_caps": 0,
"port_poe": false,
"tx_bytes-r": 0,
"rx_bytes-r": 0,
"bytes-r": 0,
"port_delta": {
"time_delta": 21
},
"enable": true,
"masked": false,
"aggregated_by": false
}
],
"radio_table": [
{
"radio": "ng",
"name": "wifi0",
"channel": "auto",
"ht": "40",
"tx_power_mode": "auto",
"tx_power": "0",
"max_txpower": 22,
"min_txpower": 6,
"nss": 3,
"min_rssi_enabled": false,
"builtin_antenna": true,
"builtin_ant_gain": 3,
"current_antenna_gain": 0,
"radio_caps": 16420,
"wlangroup_id": "574e869d4566ffb914a26843"
},
{
"radio": "na",
"name": "wifi1",
"channel": "auto",
"ht": "80",
"tx_power_mode": "high",
"tx_power": "0",
"max_txpower": 22,
"min_txpower": 6,
"nss": 3,
"min_rssi_enabled": false,
"is_11ac": true,
"has_dfs": true,
"has_fccdfs": true,
"builtin_antenna": true,
"builtin_ant_gain": 3,
"current_antenna_gain": 0,
"radio_caps": 50479140,
"wlangroup_id": "574e869d4566ffb914a26843"
}
],
"scan_radio_table": [],
"serial": "xxxyyyzzz",
"site_id": "574e86994566ffb914a2683c",
"type": "uap",
"version": "4.0.42.10433",
"vwire_table": [],
"wifi_caps": 49141,
"wlangroup_id_na": "574e869d4566ffb914a26843",
"wlangroup_id_ng": "574e869d4566ffb914a26843",
"required_version": "3.4.1",
"hw_caps": 0,
"unsupported": false,
"unsupported_reason": 0,
"sys_error_caps": 0,
"has_fan": false,
"has_temperature": false,
"device_id": "574e8bde4566ffb914a26853",
"state": 1,
"last_seen": 1562225381,
"upgradable": false,
"adoptable_when_upgraded": false,
"rollupgrade": false,
"known_cfgversion": "08cd8d6b71ebe82f",
"uptime": 3105845,
"_uptime": 3105845,
"locating": false,
"connect_request_ip": "192.168.1.8",
"connect_request_port": "43913",
"sys_stats": {
"loadavg_1": "0.04",
"loadavg_15": "0.14",
"loadavg_5": "0.10",
"mem_buffer": 0,
"mem_total": 128622592,
"mem_used": 67178496
},
"system-stats": {
"cpu": "24.7",
"mem": "52.2",
"uptime": "3105845"
},
"ssh_session_table": [],
"scanning": false,
"spectrum_scanning": false,
"guest_token": "4D630D2A1AF84771FCBB8EEB4C47E030",
"meshv3_peer_mac": "",
"satisfaction": 95,
"isolated": false,
"radio_table_stats": [
{
"name": "wifi0",
"channel": 6,
"radio": "ng",
"ast_txto": null,
"ast_cst": null,
"ast_be_xmit": 398,
"cu_total": 20,
"cu_self_rx": 14,
"cu_self_tx": 4,
"gain": 3,
"state": "RUN",
"extchannel": 1,
"tx_power": 22,
"tx_packets": 183,
"tx_retries": 108,
"num_sta": 2,
"guest-num_sta": 0,
"user-num_sta": 2
},
{
"name": "wifi1",
"channel": 36,
"radio": "na",
"ast_txto": null,
"ast_cst": null,
"ast_be_xmit": 398,
"cu_total": 12,
"cu_self_rx": 10,
"cu_self_tx": 2,
"gain": 3,
"state": "RUN",
"extchannel": 1,
"tx_power": 22,
"tx_packets": 22466,
"tx_retries": 858,
"num_sta": 4,
"guest-num_sta": 0,
"user-num_sta": 4
}
],
"uplink": {
"full_duplex": true,
"ip": "0.0.0.0",
"mac": "80:22:22:22:22:22",
"max_vlan": 96,
"name": "eth0",
"netmask": "0.0.0.0",
"num_port": 2,
"rx_bytes": 3752803309,
"rx_dropped": 102338,
"rx_errors": 0,
"rx_multicast": 0,
"rx_packets": 245302304,
"speed": 1000,
"tx_bytes": 1604707458,
"tx_dropped": 341,
"tx_errors": 0,
"tx_packets": 194278357,
"up": true,
"max_speed": 1000,
"type": "wire",
"tx_bytes-r": 9693222,
"rx_bytes-r": 92418,
"uplink_mac": "f0:22:22:22:22:22",
"uplink_remote_port": 15
},
"vap_table": [
{
"avg_client_signal": 0,
"bssid": "82:22:22:22:22:22",
"ccq": 0,
"channel": 36,
"essid": "Extra Free",
"extchannel": 1,
"id": "574e96834566ffb914a26875",
"mac_filter_rejections": 0,
"name": "ath3",
"num_satisfaction_sta": 0,
"num_sta": 0,
"radio": "na",
"radio_name": "wifi1",
"rx_bytes": 61253,
"rx_crypts": 0,
"rx_dropped": 0,
"rx_errors": 0,
"rx_frags": 0,
"rx_nwids": 47658,
"rx_packets": 576,
"rx_tcp_stats": {
"goodbytes": 0,
"lat_avg": 0,
"lat_max": 0,
"lat_min": 4294967295,
"stalls": 0
},
"satisfaction": -1,
"satisfaction_now": -1,
"state": "RUN",
"tx_bytes": 922841,
"tx_combined_retries": 0,
"tx_data_mpdu_bytes": 0,
"tx_dropped": 18128,
"tx_errors": 7,
"tx_packets": 736,
"tx_power": 22,
"tx_retries": 0,
"tx_rts_retries": 0,
"tx_success": 0,
"tx_tcp_stats": {
"goodbytes": 0,
"lat_avg": 0,
"lat_max": 0,
"lat_min": 4294967295,
"stalls": 0
},
"tx_total": 0,
"up": true,
"usage": "guest",
"wifi_tx_attempts": 0,
"wifi_tx_dropped": 0,
"wifi_tx_latency_mov": {
"avg": 0,
"max": 0,
"min": 4294967295,
"total": 0,
"total_count": 0
},
"t": "vap",
"wlanconf_id": "574e96834566ffb914a26875",
"is_guest": true,
"is_wep": false,
"ap_mac": "80:22:a8:22:22:22",
"map_id": null,
"site_id": "574e86994566ffb914a2683c"
},
{
"avg_client_signal": -65,
"bssid": "80:22:a8:22:22:22",
"ccq": 333,
"channel": 36,
"essid": "Extra Fast",
"extchannel": 1,
"id": "574e96614566ffb914a26874",
"mac_filter_rejections": 0,
"name": "ath2",
"num_satisfaction_sta": 4,
"num_sta": 4,
"radio": "na",
"radio_name": "wifi1",
"rx_bytes": 3358763522,
"rx_crypts": 161639,
"rx_dropped": 161639,
"rx_errors": 161639,
"rx_frags": 0,
"rx_nwids": 37605,
"rx_packets": 99128603,
"rx_tcp_stats": {
"goodbytes": 16243150352,
"lat_avg": 7,
"lat_max": 100,
"lat_min": 10,
"stalls": 0
},
"satisfaction": 96,
"satisfaction_now": 97,
"state": "RUN",
"tx_bytes": 834859686,
"tx_combined_retries": 9067488,
"tx_data_mpdu_bytes": 48117828355,
"tx_dropped": 109,
"tx_errors": 4076905,
"tx_packets": 111980588,
"tx_power": 22,
"tx_retries": 9067488,
"tx_rts_retries": 0,
"tx_success": 62855481,
"tx_tcp_stats": {
"goodbytes": 2154118473,
"lat_avg": 18,
"lat_max": 120,
"lat_min": 10,
"stalls": 0
},
"tx_total": 62855552,
"up": true,
"usage": "user",
"wifi_tx_attempts": 71923040,
"wifi_tx_dropped": 71,
"wifi_tx_latency_mov": {
"avg": 565,
"max": 31757,
"min": 98,
"total": 10183098,
"total_count": 18019
},
"t": "vap",
"wlanconf_id": "574e96614566ffb914a26874",
"is_guest": false,
"is_wep": false,
"ap_mac": "80:22:22:22:22:22",
"map_id": null,
"site_id": "574e86994566ffb914a2683c"
},
{
"avg_client_signal": 0,
"bssid": "82:22:a8:22:22:22",
"ccq": 0,
"channel": 6,
"essid": "Extra Free",
"extchannel": 1,
"id": "574e96834566ffb914a26875",
"mac_filter_rejections": 0,
"name": "ath1",
"num_satisfaction_sta": 0,
"num_sta": 0,
"radio": "ng",
"radio_name": "wifi0",
"reasons_bar_chart": {
"phy_rate": 0,
"signal": 0,
"sleepy_client": 0,
"sta_arp_timeout": 0,
"sta_dns_timeout": 0,
"sta_ip_timeout": 0,
"stream_eff": 0,
"tcp_latency": 0,
"tcp_packet_loss": 0,
"wifi_latency": 0,
"wifi_retries": 0
},
"reasons_bar_chart_now": {
"phy_rate": 0,
"signal": 0,
"sleepy_client": 0,
"sta_arp_timeout": 0,
"sta_dns_timeout": 0,
"sta_ip_timeout": 0,
"stream_eff": 0,
"tcp_latency": 0,
"tcp_packet_loss": 0,
"wifi_latency": 0,
"wifi_retries": 0
},
"rx_bytes": 0,
"rx_crypts": 0,
"rx_dropped": 0,
"rx_errors": 0,
"rx_frags": 0,
"rx_nwids": 120385,
"rx_packets": 0,
"rx_tcp_stats": {
"goodbytes": 0,
"lat_avg": 0,
"lat_max": 0,
"lat_min": 4294967295,
"stalls": 0
},
"satisfaction": -1,
"satisfaction_now": -1,
"state": "RUN",
"tx_bytes": 0,
"tx_combined_retries": 0,
"tx_data_mpdu_bytes": 0,
"tx_dropped": 20262,
"tx_errors": 0,
"tx_packets": 0,
"tx_power": 22,
"tx_retries": 0,
"tx_rts_retries": 0,
"tx_success": 0,
"tx_tcp_stats": {
"goodbytes": 0,
"lat_avg": 0,
"lat_max": 0,
"lat_min": 4294967295,
"stalls": 0
},
"tx_total": 0,
"up": true,
"usage": "guest",
"wifi_tx_attempts": 0,
"wifi_tx_dropped": 0,
"wifi_tx_latency_mov": {
"avg": 0,
"max": 0,
"min": 4294967295,
"total": 0,
"total_count": 0
},
"t": "vap",
"wlanconf_id": "574e96834566ffb914a26875",
"is_guest": true,
"is_wep": false,
"ap_mac": "80:22:a8:22:22:22",
"map_id": null,
"site_id": "574e86994566ffb914a2683c"
},
{
"avg_client_signal": -68,
"bssid": "80:22:22:22:22:22",
"ccq": 966,
"channel": 6,
"essid": "Extra Fast",
"extchannel": 1,
"id": "574e96614566ffb914a26874",
"mac_filter_rejections": 0,
"name": "ath0",
"num_satisfaction_sta": 2,
"num_sta": 2,
"radio": "ng",
"radio_name": "wifi0",
"reasons_bar_chart": {
"phy_rate": 0,
"signal": 0,
"sleepy_client": 0,
"sta_arp_timeout": 0,
"sta_dns_timeout": 0,
"sta_ip_timeout": 0,
"stream_eff": 0,
"tcp_latency": 0,
"tcp_packet_loss": 0,
"wifi_latency": 0,
"wifi_retries": 1
},
"reasons_bar_chart_now": {
"phy_rate": 0,
"signal": 0,
"sleepy_client": 0,
"sta_arp_timeout": 0,
"sta_dns_timeout": 0,
"sta_ip_timeout": 0,
"stream_eff": 0,
"tcp_latency": 0,
"tcp_packet_loss": 0,
"wifi_latency": 0,
"wifi_retries": 1
},
"rx_bytes": 1017366419,
"rx_crypts": 11013,
"rx_dropped": 12764,
"rx_errors": 12764,
"rx_frags": 0,
"rx_nwids": 177145,
"rx_packets": 22542668,
"rx_tcp_stats": {
"goodbytes": 114220296,
"lat_avg": 5,
"lat_max": 10,
"lat_min": 10,
"stalls": 0
},
"satisfaction": 93,
"satisfaction_now": 94,
"state": "RUN",
"tx_bytes": 965488630,
"tx_combined_retries": 8202639,
"tx_data_mpdu_bytes": 1145631754,
"tx_dropped": 43,
"tx_errors": 0,
"tx_packets": 22623798,
"tx_power": 22,
"tx_retries": 7194267,
"tx_rts_retries": 1008372,
"tx_success": 9545999,
"tx_tcp_stats": {
"goodbytes": 182912739,
"lat_avg": 5,
"lat_max": 10,
"lat_min": 10,
"stalls": 0
},
"tx_total": 9547096,
"up": true,
"usage": "user",
"wifi_tx_attempts": 16740276,
"wifi_tx_dropped": 1095,
"wifi_tx_latency_mov": {
"avg": 673,
"max": 13612,
"min": 0,
"total": 263176,
"total_count": 391
},
"t": "vap",
"wlanconf_id": "574e96614566ffb914a26874",
"is_guest": false,
"is_wep": false,
"ap_mac": "80:22:22:22:22:22",
"map_id": null,
"site_id": "574e86994566ffb914a2683c"
}
],
"downlink_table": [],
"vwire_vap_table": [],
"bytes-d": 204913232,
"tx_bytes-d": 1921569,
"rx_bytes-d": 202991663,
"bytes-r": 9757772,
"last_uplink": {
"uplink_mac": "f0:22:22:22:22:22",
"uplink_remote_port": 15
},
"stat": {
"site_id": "574e86994566ffb914a2683c",
"o": "ap",
"oid": "80:22:a8:22:22:22",
"ap": "80:22:a8:22:22:22",
"time": 1562207100000,
"datetime": "2019-07-04T02:25:00Z",
"guest-wifi0-rx_packets": 0,
"guest-wifi1-rx_packets": 0,
"user-wifi1-rx_packets": 31373230,
"user-wifi0-rx_packets": 169790,
"user-rx_packets": 31543020,
"guest-rx_packets": 0,
"wifi0-rx_packets": 169790,
"wifi1-rx_packets": 31373230,
"rx_packets": 31543020,
"guest-wifi0-rx_bytes": 0,
"guest-wifi1-rx_bytes": 0,
"user-wifi1-rx_bytes": 42049645434,
"user-wifi0-rx_bytes": 16755639,
"user-rx_bytes": 42066401073,
"guest-rx_bytes": 0,
"wifi0-rx_bytes": 16755639,
"wifi1-rx_bytes": 42049645434,
"rx_bytes": 42066401073,
"guest-wifi0-rx_errors": 0,
"guest-wifi1-rx_errors": 0,
"user-wifi1-rx_errors": 150651,
"user-wifi0-rx_errors": 0,
"user-rx_errors": 150651,
"guest-rx_errors": 0,
"wifi0-rx_errors": 0,
"wifi1-rx_errors": 150651,
"rx_errors": 150651,
"guest-wifi0-rx_dropped": 0,
"guest-wifi1-rx_dropped": 0,
"user-wifi1-rx_dropped": 150651,
"user-wifi0-rx_dropped": 0,
"user-rx_dropped": 150651,
"guest-rx_dropped": 0,
"wifi0-rx_dropped": 0,
"wifi1-rx_dropped": 150651,
"rx_dropped": 150651,
"guest-wifi0-rx_crypts": 0,
"guest-wifi1-rx_crypts": 0,
"user-wifi1-rx_crypts": 150651,
"user-wifi0-rx_crypts": 0,
"user-rx_crypts": 150651,
"guest-rx_crypts": 0,
"wifi0-rx_crypts": 0,
"wifi1-rx_crypts": 150651,
"rx_crypts": 150651,
"guest-wifi0-rx_frags": 0,
"guest-wifi1-rx_frags": 0,
"user-wifi1-rx_frags": 0,
"user-wifi0-rx_frags": 0,
"user-rx_frags": 0,
"guest-rx_frags": 0,
"wifi0-rx_frags": 0,
"wifi1-rx_frags": 0,
"rx_frags": 0,
"guest-wifi0-tx_packets": 0,
"guest-wifi1-tx_packets": 0,
"user-wifi1-tx_packets": 7125589,
"user-wifi0-tx_packets": 210389,
"user-tx_packets": 7335978,
"guest-tx_packets": 0,
"wifi0-tx_packets": 210389,
"wifi1-tx_packets": 7125589,
"tx_packets": 7335978,
"guest-wifi0-tx_bytes": 0,
"guest-wifi1-tx_bytes": 0,
"user-wifi1-tx_bytes": 3011293823,
"user-wifi0-tx_bytes": 25966558,
"user-tx_bytes": 3037260381,
"guest-tx_bytes": 0,
"wifi0-tx_bytes": 25966558,
"wifi1-tx_bytes": 3011293823,
"tx_bytes": 3037260381,
"guest-wifi0-tx_errors": 0,
"guest-wifi1-tx_errors": 0,
"user-wifi1-tx_errors": 102193,
"user-wifi0-tx_errors": 0,
"user-tx_errors": 102193,
"guest-tx_errors": 0,
"wifi0-tx_errors": 0,
"wifi1-tx_errors": 102193,
"tx_errors": 102193,
"guest-wifi0-tx_dropped": 296,
"guest-wifi1-tx_dropped": 296,
"user-wifi1-tx_dropped": 0,
"user-wifi0-tx_dropped": 0,
"user-tx_dropped": 0,
"guest-tx_dropped": 592,
"wifi0-tx_dropped": 296,
"wifi1-tx_dropped": 296,
"tx_dropped": 592,
"guest-wifi0-tx_retries": 0,
"guest-wifi1-tx_retries": 0,
"user-wifi1-tx_retries": 519734,
"user-wifi0-tx_retries": 90225,
"user-tx_retries": 609959,
"guest-tx_retries": 0,
"wifi0-tx_retries": 90225,
"wifi1-tx_retries": 519734,
"tx_retries": 609959,
"guest-wifi0-mac_filter_rejections": 0,
"guest-wifi1-mac_filter_rejections": 0,
"user-wifi1-mac_filter_rejections": 0,
"user-wifi0-mac_filter_rejections": 0,
"user-mac_filter_rejections": 0,
"guest-mac_filter_rejections": 0,
"wifi0-mac_filter_rejections": 0,
"wifi1-mac_filter_rejections": 0,
"mac_filter_rejections": 0,
"guest-wifi0-wifi_tx_attempts": 0,
"guest-wifi1-wifi_tx_attempts": 0,
"user-wifi1-wifi_tx_attempts": 4419026,
"user-wifi0-wifi_tx_attempts": 255999,
"user-wifi_tx_attempts": 4675025,
"guest-wifi_tx_attempts": 0,
"wifi0-wifi_tx_attempts": 255999,
"wifi1-wifi_tx_attempts": 4419026,
"wifi_tx_attempts": 4675025,
"guest-wifi0-wifi_tx_dropped": 0,
"guest-wifi1-wifi_tx_dropped": 0,
"user-wifi1-wifi_tx_dropped": 25,
"user-wifi0-wifi_tx_dropped": 2,
"user-wifi_tx_dropped": 27,
"guest-wifi_tx_dropped": 0,
"wifi0-wifi_tx_dropped": 2,
"wifi1-wifi_tx_dropped": 25,
"wifi_tx_dropped": 27,
"bytes": 45103661454,
"duration": 17988000,
"user-wifi1-ath2-574e96614566ffb914a26874-rx_packets": 31373230,
"user-wifi1-ath2-574e96614566ffb914a26874-rx_bytes": 42049645434,
"user-wifi1-ath2-574e96614566ffb914a26874-tx_packets": 7125589,
"user-wifi1-ath2-574e96614566ffb914a26874-tx_bytes": 3011293823,
"user-wifi1-ath2-574e96614566ffb914a26874-tx_errors": 102193,
"user-wifi1-ath2-574e96614566ffb914a26874-tx_retries": 519734,
"user-wifi1-ath2-574e96614566ffb914a26874-wifi_tx_attempts": 4419026,
"user-wifi0-ath0-574e96614566ffb914a26874-rx_packets": 169790,
"user-wifi0-ath0-574e96614566ffb914a26874-rx_bytes": 16755639,
"user-wifi0-ath0-574e96614566ffb914a26874-tx_packets": 210389,
"user-wifi0-ath0-574e96614566ffb914a26874-tx_bytes": 25966558,
"user-wifi0-ath0-574e96614566ffb914a26874-tx_retries": 90225,
"user-wifi0-ath0-574e96614566ffb914a26874-wifi_tx_attempts": 255999,
"guest-wifi1-ath3-574e96834566ffb914a26875-tx_dropped": 296,
"guest-wifi0-ath1-574e96834566ffb914a26875-tx_dropped": 296,
"user-wifi1-ath2-574e96614566ffb914a26874-rx_errors": 150651,
"user-wifi1-ath2-574e96614566ffb914a26874-rx_dropped": 150651,
"user-wifi1-ath2-574e96614566ffb914a26874-rx_crypts": 150651,
"user-wifi0-ath0-574e96614566ffb914a26874-wifi_tx_dropped": 2,
"user-wifi1-ath2-574e96614566ffb914a26874-wifi_tx_dropped": 25
},
"tx_bytes": 3037260381,
"rx_bytes": 42066401073,
"bytes": 45103661454,
"vwireEnabled": false,
"uplink_table": [],
"num_sta": 6,
"user-num_sta": 6,
"guest-num_sta": 0
}

View File

@ -0,0 +1,371 @@
{
"_id": "59a35da745663e6cc82600f6",
"adopted": true,
"cfgversion": "bf9f0335063fe6ea",
"config_network": {
"type": "dhcp",
"ip": "192.168.2.0"
},
"ethernet_table": [
{
"mac": "22:22:00:22:22:00",
"num_port": 1,
"name": "eth0"
},
{
"mac": "22:22:00:22:22:00",
"num_port": 1,
"name": "eth1"
},
{
"mac": "22:22:00:22:22:00",
"num_port": 1,
"name": "eth2"
}
],
"fw_caps": 184323,
"inform_ip": "192.168.3.1",
"inform_url": "http://security:8080/inform",
"ip": "3.1.33.7",
"led_override": "default",
"license_state": "registered",
"mac": "22:22:00:22:22:00",
"model": "UGW3",
"name": "gateway",
"outdoor_mode_override": "default",
"serial": "xxxyyyzzz",
"site_id": "574e86994566ffb914a2683c",
"type": "ugw",
"usg_caps": 786431,
"version": "4.4.41.5193700",
"required_version": "4.0.0",
"ethernet_overrides": [
{
"ifname": "eth1",
"networkgroup": "LAN"
},
{
"ifname": "eth0",
"networkgroup": "WAN"
}
],
"hw_caps": 0,
"board_rev": 16,
"unsupported": false,
"unsupported_reason": 0,
"device_id": "59a35da745663e6cc82600f6",
"state": 1,
"last_seen": 1562311857,
"upgradable": false,
"adoptable_when_upgraded": false,
"rollupgrade": false,
"known_cfgversion": "bf9f0335063fe6ea",
"uptime": 3191626,
"_uptime": 3191626,
"locating": false,
"connect_request_ip": "192.168.2.1",
"connect_request_port": "35615",
"sys_stats": {
"loadavg_1": "0.01",
"loadavg_15": "0.12",
"loadavg_5": "0.06",
"mem_buffer": 62406656,
"mem_total": 507412480,
"mem_used": 397500416
},
"system-stats": {
"cpu": "14",
"mem": "30",
"uptime": "3191066"
},
"guest_token": "83342830AE9C0641DC39DD2759C122A1",
"speedtest-status": {
"latency": 14,
"rundate": 1562310531,
"runtime": 172,
"status_download": 2,
"status_ping": 2,
"status_summary": 2,
"status_upload": 2,
"xput_download": 157.36776733398438,
"xput_upload": 37.90521240234375
},
"speedtest-status-saved": true,
"wan1": {
"tx_bytes-r": 2852355,
"rx_bytes-r": 1224743,
"bytes-r": 4077098,
"max_speed": 1000,
"type": "wire",
"name": "wan",
"ifname": "eth0",
"ip": "3.1.33.7",
"netmask": "255.255.254.0",
"mac": "22:22:00:22:22:00",
"up": true,
"speed": 1000,
"full_duplex": true,
"rx_bytes": 2648236513108,
"rx_dropped": 34030,
"rx_errors": 0,
"rx_packets": 3068347172,
"tx_bytes": 3009601283006,
"tx_dropped": 0,
"tx_errors": 0,
"tx_packets": 2859713220,
"rx_multicast": 939111,
"enable": true,
"dns": [
"1.1.1.1",
"8.8.8.8"
],
"gateway": "3.1.33.8"
},
"port_table": [
{
"name": "wan",
"ifname": "eth0",
"ip": "3.1.33.7",
"netmask": "255.255.254.0",
"mac": "22:22:00:22:22:00",
"up": true,
"speed": 1000,
"full_duplex": true,
"rx_bytes": 2648236513108,
"rx_dropped": 34030,
"rx_errors": 0,
"rx_packets": 3068347172,
"tx_bytes": 3009601283006,
"tx_dropped": 0,
"tx_errors": 0,
"tx_packets": 2859713220,
"rx_multicast": 939111,
"enable": true,
"dns": [
"216.146.35.35",
"216.146.36.36"
],
"gateway": "3.1.33.8"
},
{
"name": "lan",
"ifname": "eth1",
"ip": "192.168.2.1",
"netmask": "255.255.252.0",
"mac": "22:22:00:22:22:00",
"up": true,
"speed": 1000,
"full_duplex": true,
"rx_bytes": 2911311797255,
"rx_dropped": 3438,
"rx_errors": 0,
"rx_packets": 2659342049,
"tx_bytes": 2140222188895,
"tx_dropped": 0,
"tx_errors": 0,
"tx_packets": 2734245088,
"rx_multicast": 11929365,
"enable": true
},
{
"name": "lan2",
"ifname": "eth2",
"ip": "0.0.0.0",
"netmask": "0.0.0.0",
"mac": "22:22:00:22:22:00",
"up": false,
"speed": 0,
"full_duplex": false,
"rx_bytes": 0,
"rx_dropped": 0,
"rx_errors": 0,
"rx_packets": 0,
"tx_bytes": 0,
"tx_dropped": 0,
"tx_errors": 0,
"tx_packets": 0,
"rx_multicast": 0,
"enable": false
}
],
"network_table": [
{
"_id": "574e8de34566ffb914a26862",
"is_nat": true,
"dhcpd_dns_enabled": false,
"purpose": "guest",
"dhcpd_leasetime": "86400",
"igmp_snooping": true,
"dhcpguard_enabled": false,
"dhcpd_start": "192.168.5.1",
"enabled": true,
"dhcpd_stop": "192.168.5.254",
"dhcpd_wins_enabled": false,
"domain_name": "guest.lan",
"dhcpd_enabled": true,
"ip_subnet": "192.168.5.0/23",
"vlan": "5",
"networkgroup": "LAN",
"name": "Public Wireless",
"site_id": "574e86994566ffb914a2683c",
"dhcpd_ip_1": "",
"vlan_enabled": true,
"dhcpd_gateway_enabled": false,
"dhcpd_time_offset_enabled": false,
"ipv6_interface_type": "none",
"dhcp_relay_enabled": false,
"mac": "22:22:00:22:22:00",
"is_guest": true,
"ip": "192.168.5.0",
"up": "true",
"num_sta": 1,
"rx_bytes": 578602537,
"rx_packets": 471151,
"tx_bytes": 182318948,
"tx_packets": 239651
},
{
"_id": "59a362f645663e6cc8260133",
"is_nat": true,
"dhcpd_dns_enabled": false,
"purpose": "corporate",
"dhcpd_leasetime": 86400,
"dhcpd_start": "192.168.68.2",
"dhcpd_stop": "192.168.68.250",
"enabled": true,
"domain_name": "secure.lan",
"dhcpd_enabled": true,
"vlan": "69",
"ip_subnet": "192.168.69.1/23",
"networkgroup": "LAN",
"name": "Security Network",
"site_id": "574e86994566ffb914a2683c",
"vlan_enabled": true,
"dhcpd_ntp_1": "192.168.69.1",
"dhcpd_gateway_enabled": false,
"dhcpd_time_offset_enabled": false,
"dhcp_relay_enabled": false,
"dhcpd_ntp_enabled": true,
"ipv6_interface_type": "none",
"dhcpd_unifi_controller": "192.168.3.1",
"igmp_snooping": true,
"mac": "22:22:00:22:22:00",
"is_guest": false,
"ip": "192.168.69.1",
"up": "true",
"num_sta": 11,
"rx_bytes": 5221725,
"rx_packets": 70663,
"tx_bytes": 0,
"tx_packets": 0
},
{
"_id": "574e869d4566ffb914a26841",
"purpose": "corporate",
"dhcpd_leasetime": "86400",
"igmp_snooping": false,
"dhcpd_ntp_1": "192.168.2.1",
"dhcpguard_enabled": false,
"dhcpd_gateway_enabled": false,
"dhcpd_time_offset_enabled": false,
"dhcpd_start": "192.168.1.1",
"dhcpd_unifi_controller": "192.168.3.1",
"dhcpd_stop": "192.168.1.254",
"enabled": true,
"domain_name": "home.lan",
"dhcpd_enabled": true,
"ip_subnet": "192.168.2.1/22",
"networkgroup": "LAN",
"dhcpd_ip_1": "",
"vlan_enabled": false,
"is_nat": true,
"dhcpd_dns_enabled": false,
"dhcp_relay_enabled": false,
"dhcpd_wins_enabled": false,
"upnp_lan_enabled": true,
"dhcpd_ntp_enabled": true,
"name": "Home Network",
"site_id": "574e86994566ffb914a2683c",
"attr_no_delete": true,
"attr_hidden_id": "LAN",
"ipv6_interface_type": "none",
"mac": "22:22:00:22:22:00",
"is_guest": false,
"ip": "192.168.2.1",
"up": "true",
"num_sta": 30,
"rx_bytes": 2099754971983,
"rx_packets": 2689749160,
"tx_bytes": 2877873632166,
"tx_packets": 2579198457
}
],
"uplink": {
"drops": 40,
"enable": true,
"full_duplex": true,
"gateways": [
"3.1.33.8"
],
"ip": "3.1.33.7",
"latency": 103,
"mac": "22:22:00:22:22:00",
"name": "eth0",
"nameservers": [
"1.1.1.1",
"8.8.8.8"
],
"netmask": "255.255.254.0",
"num_port": 1,
"rx_bytes": 2648236513108,
"rx_dropped": 34030,
"rx_errors": 0,
"rx_multicast": 939111,
"rx_packets": 3068347172,
"speed": 1000,
"speedtest_lastrun": 1562310531,
"speedtest_ping": 14,
"speedtest_status": "Success",
"tx_bytes": 3009601283006,
"tx_dropped": 0,
"tx_errors": 0,
"tx_packets": 2859713220,
"up": true,
"uptime": 559088,
"xput_down": 157.368,
"xput_up": 37.905,
"tx_bytes-r": 2852355,
"rx_bytes-r": 1224743,
"bytes-r": 4077098,
"max_speed": 1000,
"type": "wire"
},
"stat": {
"site_id": "574e86994566ffb914a2683c",
"o": "gw",
"oid": "22:22:00:22:22:00",
"gw": "22:22:00:22:22:00",
"time": 1562207100000,
"datetime": "2019-07-04T02:25:00Z",
"duration": 104466000,
"wan-rx_packets": 151387924,
"wan-rx_bytes": 111251311739,
"wan-tx_packets": 182985900,
"wan-tx_bytes": 230372237709,
"lan-rx_packets": 173953163,
"lan-rx_bytes": 226862410885,
"lan-tx_packets": 137029474,
"lan-tx_bytes": 89478206254,
"wan-rx_dropped": 561,
"lan-rx_dropped": 29
},
"tx_bytes": 2648236513108,
"rx_bytes": 3009601283006,
"bytes": 5657837796114,
"num_sta": 41,
"user-num_sta": 41,
"guest-num_sta": 0,
"num_desktop": 7,
"num_mobile": 2,
"num_handheld": 8
}

File diff suppressed because it is too large Load Diff

9
core/unifi/v4/go.mod Normal file
View File

@ -0,0 +1,9 @@
module golift.io/unifi
go 1.13
require (
github.com/davecgh/go-spew v1.1.1
github.com/pmezard/go-difflib v1.0.0
github.com/stretchr/testify v1.4.0
)

11
core/unifi/v4/go.sum Normal file
View File

@ -0,0 +1,11 @@
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

150
core/unifi/v4/ids.go Normal file
View File

@ -0,0 +1,150 @@
package unifi
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"time"
)
// IDSList contains a list that contains all of the IDS Events on a controller.
type IDSList []*IDS
// IDS holds an Intrusion Prevention System Event.
type IDS struct {
SourceName string `json:"-"`
ID string `json:"_id"`
Archived FlexBool `json:"archived"`
Timestamp int64 `json:"timestamp"`
FlowID int64 `json:"flow_id"`
InIface string `json:"in_iface"`
EventType string `json:"event_type"`
SrcIP string `json:"src_ip"`
SrcMac string `json:"src_mac"`
SrcPort int `json:"src_port,omitempty"`
DestIP string `json:"dest_ip"`
DstMac string `json:"dst_mac"`
DestPort int `json:"dest_port,omitempty"`
Proto string `json:"proto"`
AppProto string `json:"app_proto,omitempty"`
Host string `json:"host"`
Usgip string `json:"usgip"`
UniqueAlertid string `json:"unique_alertid"`
SrcipCountry string `json:"srcipCountry"`
DstipCountry FlexBool `json:"dstipCountry"`
UsgipCountry string `json:"usgipCountry"`
SrcipGeo struct {
ContinentCode string `json:"continent_code"`
CountryCode string `json:"country_code"`
CountryCode3 string `json:"country_code3"`
CountryName string `json:"country_name"`
Region string `json:"region"`
City string `json:"city"`
PostalCode string `json:"postal_code"`
Latitude float64 `json:"latitude"`
Longitude float64 `json:"longitude"`
DmaCode int64 `json:"dma_code"`
AreaCode int64 `json:"area_code"`
} `json:"srcipGeo"`
DstipGeo bool `json:"dstipGeo"`
UsgipGeo struct {
ContinentCode string `json:"continent_code"`
CountryCode string `json:"country_code"`
CountryCode3 string `json:"country_code3"`
CountryName string `json:"country_name"`
Region string `json:"region"`
City string `json:"city"`
PostalCode string `json:"postal_code"`
Latitude float64 `json:"latitude"`
Longitude float64 `json:"longitude"`
DmaCode int64 `json:"dma_code"`
AreaCode int64 `json:"area_code"`
} `json:"usgipGeo"`
SrcipASN string `json:"srcipASN"`
DstipASN string `json:"dstipASN"`
UsgipASN string `json:"usgipASN"`
Catname string `json:"catname"`
InnerAlertAction string `json:"inner_alert_action"`
InnerAlertGID int64 `json:"inner_alert_gid"`
InnerAlertSignatureID int64 `json:"inner_alert_signature_id"`
InnerAlertRev int64 `json:"inner_alert_rev"`
InnerAlertSignature string `json:"inner_alert_signature"`
InnerAlertCategory string `json:"inner_alert_category"`
InnerAlertSeverity int64 `json:"inner_alert_severity"`
Key string `json:"key"`
Subsystem string `json:"subsystem"`
SiteID string `json:"site_id"`
SiteName string `json:"-"`
Time int64 `json:"time"`
Datetime time.Time `json:"datetime"`
Msg string `json:"msg"`
IcmpType int64 `json:"icmp_type,omitempty"`
IcmpCode int64 `json:"icmp_code,omitempty"`
}
// GetIDS returns Intrusion Detection Systems events.
// Returns all events that happened in site between from and to.
func (u *Unifi) GetIDS(sites Sites, from, to time.Time) ([]*IDS, error) {
data := []*IDS{}
for _, site := range sites {
u.DebugLog("Polling Controller for IDS/IPS Data, site %s (%s) ", site.Name, site.Desc)
ids, err := u.GetSiteIDS(site, from, to)
if err != nil {
return data, err
}
for i := range ids {
ids[i].SourceName = u.URL
}
data = append(data, ids...)
}
return data, nil
}
// GetSiteIDS is a helper to offload the for-loop work.
// This method retreives the Intrusion Detection System Data for 1 Site.
func (u *Unifi) GetSiteIDS(site *Site, from, to time.Time) ([]*IDS, error) {
var response struct {
Data []*IDS `json:"data"`
}
URIpath := fmt.Sprintf(APIIPSEvents, site.Name)
params := fmt.Sprintf(`{"start":"%v000","end":"%v000","_limit":50000}`, from.Unix(), to.Unix())
req, err := u.UniReq(URIpath, params)
if err != nil {
return nil, err
}
resp, err := u.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("invalid status code from server %s", resp.Status)
}
if err := json.Unmarshal(body, &response); err != nil {
return nil, err
}
for i := range response.Data {
response.Data[i].SiteName = site.SiteName
}
return response.Data, nil
}

124
core/unifi/v4/site.go Normal file
View File

@ -0,0 +1,124 @@
package unifi
import (
"fmt"
"strings"
)
// GetSites returns a list of configured sites on the UniFi controller.
func (u *Unifi) GetSites() (Sites, error) {
var response struct {
Data []*Site `json:"data"`
}
if err := u.GetData(APISiteList, &response); err != nil {
return nil, err
}
sites := []string{} // used for debug log only
for i, d := range response.Data {
// Add special SourceName value.
response.Data[i].SourceName = u.URL
// If the human name is missing (description), set it to the cryptic name.
response.Data[i].Desc = pick(d.Desc, d.Name)
// Add the custom site name to each site. used as a Grafana filter somewhere.
response.Data[i].SiteName = d.Desc + " (" + d.Name + ")"
sites = append(sites, d.Name) // used for debug log only
}
u.DebugLog("Found %d site(s): %s", len(sites), strings.Join(sites, ","))
return response.Data, nil
}
// GetSiteDPI garners dpi data for sites.
func (u *Unifi) GetSiteDPI(sites Sites) ([]*DPITable, error) {
data := []*DPITable{}
for _, site := range sites {
u.DebugLog("Polling Controller, retreiving Site DPI data, site %s (%s) ", site.Name, site.Desc)
var response struct {
Data []*DPITable `json:"data"`
}
siteDPIpath := fmt.Sprintf(APISiteDPI, site.Name)
if err := u.GetData(siteDPIpath, &response, `{"type":"by_app"}`); err != nil {
return nil, err
}
if l := len(response.Data); l > 1 {
return nil, fmt.Errorf("dpi data table contains more than 1 item; please open a bug report")
} else if l != 1 {
continue
}
response.Data[0].SourceName = site.SourceName
response.Data[0].SiteName = site.SiteName
data = append(data, response.Data[0])
}
return data, nil
}
// Sites is a struct to match Devices and Clients.
type Sites []*Site
// Site represents a site's data.
type Site struct {
SourceName string `json:"-"`
ID string `json:"_id"`
Name string `json:"name"`
Desc string `json:"desc"`
SiteName string `json:"-"`
AttrHiddenID string `json:"attr_hidden_id"`
AttrNoDelete FlexBool `json:"attr_no_delete"`
Health []struct {
Subsystem string `json:"subsystem"`
NumUser FlexInt `json:"num_user,omitempty"`
NumGuest FlexInt `json:"num_guest,omitempty"`
NumIot FlexInt `json:"num_iot,omitempty"`
TxBytesR FlexInt `json:"tx_bytes-r,omitempty"`
RxBytesR FlexInt `json:"rx_bytes-r,omitempty"`
Status string `json:"status"`
NumAp FlexInt `json:"num_ap,omitempty"`
NumAdopted FlexInt `json:"num_adopted,omitempty"`
NumDisabled FlexInt `json:"num_disabled,omitempty"`
NumDisconnected FlexInt `json:"num_disconnected,omitempty"`
NumPending FlexInt `json:"num_pending,omitempty"`
NumGw FlexInt `json:"num_gw,omitempty"`
WanIP string `json:"wan_ip,omitempty"`
Gateways []string `json:"gateways,omitempty"`
Netmask string `json:"netmask,omitempty"`
Nameservers []string `json:"nameservers,omitempty"`
NumSta FlexInt `json:"num_sta,omitempty"`
GwMac string `json:"gw_mac,omitempty"`
GwName string `json:"gw_name,omitempty"`
GwSystemStats struct {
CPU FlexInt `json:"cpu"`
Mem FlexInt `json:"mem"`
Uptime FlexInt `json:"uptime"`
} `json:"gw_system-stats,omitempty"`
GwVersion string `json:"gw_version,omitempty"`
Latency FlexInt `json:"latency,omitempty"`
Uptime FlexInt `json:"uptime,omitempty"`
Drops FlexInt `json:"drops,omitempty"`
XputUp FlexInt `json:"xput_up,omitempty"`
XputDown FlexInt `json:"xput_down,omitempty"`
SpeedtestStatus string `json:"speedtest_status,omitempty"`
SpeedtestLastrun FlexInt `json:"speedtest_lastrun,omitempty"`
SpeedtestPing FlexInt `json:"speedtest_ping,omitempty"`
LanIP string `json:"lan_ip,omitempty"`
NumSw FlexInt `json:"num_sw,omitempty"`
RemoteUserEnabled FlexBool `json:"remote_user_enabled,omitempty"`
RemoteUserNumActive FlexInt `json:"remote_user_num_active,omitempty"`
RemoteUserNumInactive FlexInt `json:"remote_user_num_inactive,omitempty"`
RemoteUserRxBytes FlexInt `json:"remote_user_rx_bytes,omitempty"`
RemoteUserTxBytes FlexInt `json:"remote_user_tx_bytes,omitempty"`
RemoteUserRxPackets FlexInt `json:"remote_user_rx_packets,omitempty"`
RemoteUserTxPackets FlexInt `json:"remote_user_tx_packets,omitempty"`
SiteToSiteEnabled FlexBool `json:"site_to_site_enabled,omitempty"`
} `json:"health"`
NumNewAlarms FlexInt `json:"num_new_alarms"`
}

132
core/unifi/v4/types.go Normal file
View File

@ -0,0 +1,132 @@
package unifi
import (
"encoding/json"
"fmt"
"net/http"
"strconv"
"strings"
)
// This is a list of unifi API paths.
// The %s in each string must be replaced with a Site.Name.
const (
// APIStatusPath shows Controller version.
APIStatusPath string = "/status"
// APISiteList is the path to the api site list.
APISiteList string = "/api/stat/sites"
// APISiteDPI is site DPI data.
APISiteDPI string = "/api/s/%s/stat/sitedpi"
// APISiteDPI is site DPI data.
APIClientDPI string = "/api/s/%s/stat/stadpi"
// APIClientPath is Unifi Clients API Path
APIClientPath string = "/api/s/%s/stat/sta"
// APIDevicePath is where we get data about Unifi devices.
APIDevicePath string = "/api/s/%s/stat/device"
// APINetworkPath contains network-configuration data. Not really graphable.
APINetworkPath string = "/api/s/%s/rest/networkconf"
// APIUserGroupPath contains usergroup configurations.
APIUserGroupPath string = "/api/s/%s/rest/usergroup"
// APILoginPath is Unifi Controller Login API Path
APILoginPath string = "/api/login"
// APIIPSEvents returns Intrusion Detection Systems Events
APIIPSEvents string = "/api/s/%s/stat/ips/event"
)
// Logger is a base type to deal with changing log outputs. Create a logger
// that matches this interface to capture debug and error logs.
type Logger func(msg string, fmt ...interface{})
// discardLogs is the default debug logger.
func discardLogs(msg string, v ...interface{}) {
// do nothing.
}
// Devices contains a list of all the unifi devices from a controller.
// Contains Access points, security gateways and switches.
type Devices struct {
UAPs []*UAP
USGs []*USG
USWs []*USW
UDMs []*UDM
}
// Config is the data passed into our library. This configures things and allows
// us to connect to a controller and write log messages.
type Config struct {
User string
Pass string
URL string
VerifySSL bool
ErrorLog Logger
DebugLog Logger
}
// Unifi is what you get in return for providing a password! Unifi represents
// a controller that you can make authenticated requests to. Use this to make
// additional requests for devices, clients or other custom data. Do not set
// the loggers to nil. Set them to DiscardLogs if you want no logs.
type Unifi struct {
*http.Client
*Config
*server
}
// server is the /status endpoint from the Unifi controller.
type server struct {
Up FlexBool `json:"up"`
ServerVersion string `json:"server_version"`
UUID string `json:"uuid"`
}
// FlexInt provides a container and unmarshalling for fields that may be
// numbers or strings in the Unifi API.
type FlexInt struct {
Val float64
Txt string
}
// UnmarshalJSON converts a string or number to an integer.
// Generally, do call this directly, it's used in the json interface.
func (f *FlexInt) UnmarshalJSON(b []byte) error {
var unk interface{}
if err := json.Unmarshal(b, &unk); err != nil {
return err
}
switch i := unk.(type) {
case float64:
f.Val = i
f.Txt = strconv.FormatFloat(i, 'f', -1, 64)
case string:
f.Txt = i
f.Val, _ = strconv.ParseFloat(i, 64)
case nil:
f.Txt = "0"
f.Val = 0
default:
return fmt.Errorf("cannot unmarshal to FlexInt: %s", b)
}
return nil
}
// FlexBool provides a container and unmarshalling for fields that may be
// boolean or strings in the Unifi API.
type FlexBool struct {
Val bool
Txt string
}
// UnmarshalJSON method converts armed/disarmed, yes/no, active/inactive or 0/1 to true/false.
// Really it converts ready, ok, up, t, armed, yes, active, enabled, 1, true to true. Anything else is false.
func (f *FlexBool) UnmarshalJSON(b []byte) error {
f.Txt = strings.Trim(string(b), `"`)
f.Val = f.Txt == "1" || strings.EqualFold(f.Txt, "true") || strings.EqualFold(f.Txt, "yes") ||
strings.EqualFold(f.Txt, "t") || strings.EqualFold(f.Txt, "armed") || strings.EqualFold(f.Txt, "active") ||
strings.EqualFold(f.Txt, "enabled") || strings.EqualFold(f.Txt, "ready") || strings.EqualFold(f.Txt, "up") ||
strings.EqualFold(f.Txt, "ok")
return nil
}

View File

@ -0,0 +1,41 @@
package unifi
import (
"encoding/json"
"testing"
"github.com/stretchr/testify/assert"
)
func TestFlexInt(t *testing.T) {
t.Parallel()
a := assert.New(t)
five, seven := 5, 7
var r struct {
Five FlexInt `json:"five"`
Seven FlexInt `json:"seven"`
Auto FlexInt `json:"auto"`
Channel FlexInt `json:"channel"`
Nil FlexInt `json:"nil"`
}
// test unmarshalling the custom type three times with different values.
a.Nil(json.Unmarshal([]byte(`{"five": "5", "seven": 7, "auto": "auto", "nil": null}`), &r))
// test number in string.
a.EqualValues(five, r.Five.Val)
a.EqualValues("5", r.Five.Txt)
// test number.
a.EqualValues(seven, r.Seven.Val)
a.EqualValues("7", r.Seven.Txt)
// test string.
a.EqualValues(0, r.Auto.Val)
a.EqualValues("auto", r.Auto.Txt)
// test (error) struct.
a.NotNil(json.Unmarshal([]byte(`{"channel": {}}`), &r),
"a non-string and non-number must produce an error.")
a.EqualValues(0, r.Channel.Val)
// test null.
a.EqualValues(0, r.Nil.Val)
a.EqualValues("0", r.Nil.Txt)
}

566
core/unifi/v4/uap.go Normal file
View File

@ -0,0 +1,566 @@
package unifi
import (
"encoding/json"
"time"
)
// UAP represents all the data from the Ubiquiti Controller for a Unifi Access Point.
// This was auto generated then edited by hand to get all the data types right.
type UAP struct {
SourceName string `json:"-"`
ID string `json:"_id"`
Adopted FlexBool `json:"adopted"`
AntennaTable []struct {
Default FlexBool `json:"default"`
ID FlexInt `json:"id"`
Name string `json:"name"`
Wifi0Gain FlexInt `json:"wifi0_gain"`
Wifi1Gain FlexInt `json:"wifi1_gain"`
} `json:"antenna_table"`
BandsteeringMode string `json:"bandsteering_mode,omitempty"`
BoardRev int `json:"board_rev"`
Cfgversion string `json:"cfgversion"`
ConfigNetwork struct {
Type string `json:"type"`
IP string `json:"ip"`
} `json:"config_network"`
CountrycodeTable []int `json:"countrycode_table"`
EthernetTable []struct {
Mac string `json:"mac"`
NumPort FlexInt `json:"num_port"`
Name string `json:"name"`
} `json:"ethernet_table"`
FwCaps int `json:"fw_caps"`
HasEth1 FlexBool `json:"has_eth1"`
HasSpeaker FlexBool `json:"has_speaker"`
InformIP string `json:"inform_ip"`
InformURL string `json:"inform_url"`
IP string `json:"ip"`
LedOverride string `json:"led_override"`
Mac string `json:"mac"`
MeshStaVapEnabled FlexBool `json:"mesh_sta_vap_enabled"`
Model string `json:"model"`
Name string `json:"name"`
OutdoorModeOverride string `json:"outdoor_mode_override"`
PortTable []Port `json:"port_table"`
RadioTable RadioTable `json:"radio_table"`
ScanRadioTable []interface{} `json:"scan_radio_table"`
Serial string `json:"serial"`
SiteID string `json:"site_id"`
SiteName string `json:"-"`
Type string `json:"type"`
Version string `json:"version"`
VwireTable []interface{} `json:"vwire_table"`
WifiCaps int `json:"wifi_caps"`
WlangroupIDNa string `json:"wlangroup_id_na"`
WlangroupIDNg string `json:"wlangroup_id_ng"`
RequiredVersion string `json:"required_version"`
HwCaps int `json:"hw_caps"`
Unsupported FlexBool `json:"unsupported"`
UnsupportedReason FlexInt `json:"unsupported_reason"`
SysErrorCaps int `json:"sys_error_caps"`
HasFan FlexBool `json:"has_fan"`
HasTemperature FlexBool `json:"has_temperature"`
DeviceID string `json:"device_id"`
State int `json:"state"`
LastSeen FlexInt `json:"last_seen"`
Upgradable FlexBool `json:"upgradable"`
AdoptableWhenUpgraded FlexBool `json:"adoptable_when_upgraded"`
Rollupgrade FlexBool `json:"rollupgrade"`
KnownCfgversion string `json:"known_cfgversion"`
Uptime FlexInt `json:"uptime"`
UUptime FlexInt `json:"_uptime"`
Locating FlexBool `json:"locating"`
ConnectRequestIP string `json:"connect_request_ip"`
ConnectRequestPort string `json:"connect_request_port"`
SysStats SysStats `json:"sys_stats"`
SystemStats SystemStats `json:"system-stats"`
SSHSessionTable []interface{} `json:"ssh_session_table"`
Scanning FlexBool `json:"scanning"`
SpectrumScanning FlexBool `json:"spectrum_scanning"`
GuestToken string `json:"guest_token"`
Meshv3PeerMac string `json:"meshv3_peer_mac"`
Satisfaction FlexInt `json:"satisfaction"`
Isolated FlexBool `json:"isolated"`
RadioTableStats RadioTableStats `json:"radio_table_stats"`
Uplink struct {
FullDuplex FlexBool `json:"full_duplex"`
IP string `json:"ip"`
Mac string `json:"mac"`
MaxVlan int `json:"max_vlan"`
Name string `json:"name"`
Netmask string `json:"netmask"`
NumPort int `json:"num_port"`
RxBytes FlexInt `json:"rx_bytes"`
RxDropped FlexInt `json:"rx_dropped"`
RxErrors FlexInt `json:"rx_errors"`
RxMulticast FlexInt `json:"rx_multicast"`
RxPackets FlexInt `json:"rx_packets"`
Speed FlexInt `json:"speed"`
TxBytes FlexInt `json:"tx_bytes"`
TxDropped FlexInt `json:"tx_dropped"`
TxErrors FlexInt `json:"tx_errors"`
TxPackets FlexInt `json:"tx_packets"`
Up FlexBool `json:"up"`
MaxSpeed FlexInt `json:"max_speed"`
Type string `json:"type"`
TxBytesR FlexInt `json:"tx_bytes-r"`
RxBytesR FlexInt `json:"rx_bytes-r"`
UplinkMac string `json:"uplink_mac"`
UplinkRemotePort int `json:"uplink_remote_port"`
} `json:"uplink"`
VapTable VapTable `json:"vap_table"`
DownlinkTable []struct {
PortIdx int `json:"port_idx"`
Speed int `json:"speed"`
FullDuplex bool `json:"full_duplex"`
Mac string `json:"mac"`
} `json:"downlink_table,omitempty"`
VwireVapTable []interface{} `json:"vwire_vap_table"`
BytesD FlexInt `json:"bytes-d"`
TxBytesD FlexInt `json:"tx_bytes-d"`
RxBytesD FlexInt `json:"rx_bytes-d"`
BytesR FlexInt `json:"bytes-r"`
LastUplink struct {
UplinkMac string `json:"uplink_mac"`
UplinkRemotePort int `json:"uplink_remote_port"`
} `json:"last_uplink"`
Stat UAPStat `json:"stat"`
TxBytes FlexInt `json:"tx_bytes"`
RxBytes FlexInt `json:"rx_bytes"`
Bytes FlexInt `json:"bytes"`
VwireEnabled FlexBool `json:"vwireEnabled"`
UplinkTable []interface{} `json:"uplink_table"`
NumSta FlexInt `json:"num_sta"`
UserNumSta FlexInt `json:"user-num_sta"`
GuestNumSta FlexInt `json:"guest-num_sta"`
TwoPhaseAdopt FlexBool `json:"two_phase_adopt,omitempty"`
}
// UAPStat holds the "stat" data for an access point.
// This is split out because of a JSON data format change from 5.10 to 5.11.
type UAPStat struct {
*Ap
}
// Ap is a subtype of UAPStat to make unmarshalling of different controller versions possible.
type Ap struct {
SiteID string `json:"site_id"`
O string `json:"o"`
Oid string `json:"oid"`
Ap string `json:"ap"`
Time FlexInt `json:"time"`
Datetime time.Time `json:"datetime"`
Bytes FlexInt `json:"bytes"`
Duration FlexInt `json:"duration"`
WifiTxDropped FlexInt `json:"wifi_tx_dropped"`
RxErrors FlexInt `json:"rx_errors"`
RxDropped FlexInt `json:"rx_dropped"`
RxFrags FlexInt `json:"rx_frags"`
RxCrypts FlexInt `json:"rx_crypts"`
TxPackets FlexInt `json:"tx_packets"`
TxBytes FlexInt `json:"tx_bytes"`
TxErrors FlexInt `json:"tx_errors"`
TxDropped FlexInt `json:"tx_dropped"`
TxRetries FlexInt `json:"tx_retries"`
RxPackets FlexInt `json:"rx_packets"`
RxBytes FlexInt `json:"rx_bytes"`
UserRxDropped FlexInt `json:"user-rx_dropped"`
GuestRxDropped FlexInt `json:"guest-rx_dropped"`
UserRxErrors FlexInt `json:"user-rx_errors"`
GuestRxErrors FlexInt `json:"guest-rx_errors"`
UserRxPackets FlexInt `json:"user-rx_packets"`
GuestRxPackets FlexInt `json:"guest-rx_packets"`
UserRxBytes FlexInt `json:"user-rx_bytes"`
GuestRxBytes FlexInt `json:"guest-rx_bytes"`
UserRxCrypts FlexInt `json:"user-rx_crypts"`
GuestRxCrypts FlexInt `json:"guest-rx_crypts"`
UserRxFrags FlexInt `json:"user-rx_frags"`
GuestRxFrags FlexInt `json:"guest-rx_frags"`
UserTxPackets FlexInt `json:"user-tx_packets"`
GuestTxPackets FlexInt `json:"guest-tx_packets"`
UserTxBytes FlexInt `json:"user-tx_bytes"`
GuestTxBytes FlexInt `json:"guest-tx_bytes"`
UserTxErrors FlexInt `json:"user-tx_errors"`
GuestTxErrors FlexInt `json:"guest-tx_errors"`
UserTxDropped FlexInt `json:"user-tx_dropped"`
GuestTxDropped FlexInt `json:"guest-tx_dropped"`
UserTxRetries FlexInt `json:"user-tx_retries"`
GuestTxRetries FlexInt `json:"guest-tx_retries"`
MacFilterRejections FlexInt `json:"mac_filter_rejections"`
UserMacFilterRejections FlexInt `json:"user-mac_filter_rejections"`
GuestMacFilterRejections FlexInt `json:"guest-mac_filter_rejections"`
WifiTxAttempts FlexInt `json:"wifi_tx_attempts"`
UserWifiTxDropped FlexInt `json:"user-wifi_tx_dropped"`
GuestWifiTxDropped FlexInt `json:"guest-wifi_tx_dropped"`
UserWifiTxAttempts FlexInt `json:"user-wifi_tx_attempts"`
GuestWifiTxAttempts FlexInt `json:"guest-wifi_tx_attempts"`
// UAP-AC-PRO names, others may differ.
/* These are all in VAP TABLE */
/*
GuestWifi0RxPackets FlexInt `json:"guest-wifi0-rx_packets"`
GuestWifi1RxPackets FlexInt `json:"guest-wifi1-rx_packets"`
UserWifi1RxPackets FlexInt `json:"user-wifi1-rx_packets"`
UserWifi0RxPackets FlexInt `json:"user-wifi0-rx_packets"`
Wifi0RxPackets FlexInt `json:"wifi0-rx_packets"`
Wifi1RxPackets FlexInt `json:"wifi1-rx_packets"`
GuestWifi0RxBytes FlexInt `json:"guest-wifi0-rx_bytes"`
GuestWifi1RxBytes FlexInt `json:"guest-wifi1-rx_bytes"`
UserWifi1RxBytes FlexInt `json:"user-wifi1-rx_bytes"`
UserWifi0RxBytes FlexInt `json:"user-wifi0-rx_bytes"`
Wifi0RxBytes FlexInt `json:"wifi0-rx_bytes"`
Wifi1RxBytes FlexInt `json:"wifi1-rx_bytes"`
GuestWifi0RxErrors FlexInt `json:"guest-wifi0-rx_errors"`
GuestWifi1RxErrors FlexInt `json:"guest-wifi1-rx_errors"`
UserWifi1RxErrors FlexInt `json:"user-wifi1-rx_errors"`
UserWifi0RxErrors FlexInt `json:"user-wifi0-rx_errors"`
Wifi0RxErrors FlexInt `json:"wifi0-rx_errors"`
Wifi1RxErrors FlexInt `json:"wifi1-rx_errors"`
GuestWifi0RxDropped FlexInt `json:"guest-wifi0-rx_dropped"`
GuestWifi1RxDropped FlexInt `json:"guest-wifi1-rx_dropped"`
UserWifi1RxDropped FlexInt `json:"user-wifi1-rx_dropped"`
UserWifi0RxDropped FlexInt `json:"user-wifi0-rx_dropped"`
Wifi0RxDropped FlexInt `json:"wifi0-rx_dropped"`
Wifi1RxDropped FlexInt `json:"wifi1-rx_dropped"`
GuestWifi0RxCrypts FlexInt `json:"guest-wifi0-rx_crypts"`
GuestWifi1RxCrypts FlexInt `json:"guest-wifi1-rx_crypts"`
UserWifi1RxCrypts FlexInt `json:"user-wifi1-rx_crypts"`
UserWifi0RxCrypts FlexInt `json:"user-wifi0-rx_crypts"`
Wifi0RxCrypts FlexInt `json:"wifi0-rx_crypts"`
Wifi1RxCrypts FlexInt `json:"wifi1-rx_crypts"`
GuestWifi0RxFrags FlexInt `json:"guest-wifi0-rx_frags"`
GuestWifi1RxFrags FlexInt `json:"guest-wifi1-rx_frags"`
UserWifi1RxFrags FlexInt `json:"user-wifi1-rx_frags"`
UserWifi0RxFrags FlexInt `json:"user-wifi0-rx_frags"`
Wifi0RxFrags FlexInt `json:"wifi0-rx_frags"`
Wifi1RxFrags FlexInt `json:"wifi1-rx_frags"`
GuestWifi0TxPackets FlexInt `json:"guest-wifi0-tx_packets"`
GuestWifi1TxPackets FlexInt `json:"guest-wifi1-tx_packets"`
UserWifi1TxPackets FlexInt `json:"user-wifi1-tx_packets"`
UserWifi0TxPackets FlexInt `json:"user-wifi0-tx_packets"`
Wifi0TxPackets FlexInt `json:"wifi0-tx_packets"`
Wifi1TxPackets FlexInt `json:"wifi1-tx_packets"`
GuestWifi0TxBytes FlexInt `json:"guest-wifi0-tx_bytes"`
GuestWifi1TxBytes FlexInt `json:"guest-wifi1-tx_bytes"`
UserWifi1TxBytes FlexInt `json:"user-wifi1-tx_bytes"`
UserWifi0TxBytes FlexInt `json:"user-wifi0-tx_bytes"`
Wifi0TxBytes FlexInt `json:"wifi0-tx_bytes"`
Wifi1TxBytes FlexInt `json:"wifi1-tx_bytes"`
GuestWifi0TxErrors FlexInt `json:"guest-wifi0-tx_errors"`
GuestWifi1TxErrors FlexInt `json:"guest-wifi1-tx_errors"`
UserWifi1TxErrors FlexInt `json:"user-wifi1-tx_errors"`
UserWifi0TxErrors FlexInt `json:"user-wifi0-tx_errors"`
Wifi0TxErrors FlexInt `json:"wifi0-tx_errors"`
Wifi1TxErrors FlexInt `json:"wifi1-tx_errors"`
GuestWifi0TxDropped FlexInt `json:"guest-wifi0-tx_dropped"`
GuestWifi1TxDropped FlexInt `json:"guest-wifi1-tx_dropped"`
UserWifi1TxDropped FlexInt `json:"user-wifi1-tx_dropped"`
UserWifi0TxDropped FlexInt `json:"user-wifi0-tx_dropped"`
Wifi0TxDropped FlexInt `json:"wifi0-tx_dropped"`
Wifi1TxDropped FlexInt `json:"wifi1-tx_dropped"`
GuestWifi0TxRetries FlexInt `json:"guest-wifi0-tx_retries"`
GuestWifi1TxRetries FlexInt `json:"guest-wifi1-tx_retries"`
UserWifi1TxRetries FlexInt `json:"user-wifi1-tx_retries"`
UserWifi0TxRetries FlexInt `json:"user-wifi0-tx_retries"`
Wifi0TxRetries FlexInt `json:"wifi0-tx_retries"`
Wifi1TxRetries FlexInt `json:"wifi1-tx_retries"`
GuestWifi0MacFilterRejections FlexInt `json:"guest-wifi0-mac_filter_rejections"`
GuestWifi1MacFilterRejections FlexInt `json:"guest-wifi1-mac_filter_rejections"`
UserWifi1MacFilterRejections FlexInt `json:"user-wifi1-mac_filter_rejections"`
UserWifi0MacFilterRejections FlexInt `json:"user-wifi0-mac_filter_rejections"`
Wifi0MacFilterRejections FlexInt `json:"wifi0-mac_filter_rejections"`
Wifi1MacFilterRejections FlexInt `json:"wifi1-mac_filter_rejections"`
GuestWifi0WifiTxAttempts FlexInt `json:"guest-wifi0-wifi_tx_attempts"`
GuestWifi1WifiTxAttempts FlexInt `json:"guest-wifi1-wifi_tx_attempts"`
UserWifi1WifiTxAttempts FlexInt `json:"user-wifi1-wifi_tx_attempts"`
UserWifi0WifiTxAttempts FlexInt `json:"user-wifi0-wifi_tx_attempts"`
Wifi0WifiTxAttempts FlexInt `json:"wifi0-wifi_tx_attempts"`
Wifi1WifiTxAttempts FlexInt `json:"wifi1-wifi_tx_attempts"`
GuestWifi0WifiTxDropped FlexInt `json:"guest-wifi0-wifi_tx_dropped"`
GuestWifi1WifiTxDropped FlexInt `json:"guest-wifi1-wifi_tx_dropped"`
UserWifi1WifiTxDropped FlexInt `json:"user-wifi1-wifi_tx_dropped"`
UserWifi0WifiTxDropped FlexInt `json:"user-wifi0-wifi_tx_dropped"`
Wifi0WifiTxDropped FlexInt `json:"wifi0-wifi_tx_dropped"`
Wifi1WifiTxDropped FlexInt `json:"wifi1-wifi_tx_dropped"`
// UDM Names
GuestRa0RxPackets FlexInt `json:"guest-ra0-rx_packets"`
UserRa0RxPackets FlexInt `json:"user-ra0-rx_packets"`
Ra0RxPackets FlexInt `json:"ra0-rx_packets"`
GuestRa0RxBytes FlexInt `json:"guest-ra0-rx_bytes"`
UserRa0RxBytes FlexInt `json:"user-ra0-rx_bytes"`
Ra0RxBytes FlexInt `json:"ra0-rx_bytes"`
GuestRa0RxErrors FlexInt `json:"guest-ra0-rx_errors"`
UserRa0RxErrors FlexInt `json:"user-ra0-rx_errors"`
Ra0RxErrors FlexInt `json:"ra0-rx_errors"`
GuestRa0RxDropped FlexInt `json:"guest-ra0-rx_dropped"`
UserRa0RxDropped FlexInt `json:"user-ra0-rx_dropped"`
Ra0RxDropped FlexInt `json:"ra0-rx_dropped"`
GuestRa0RxCrypts FlexInt `json:"guest-ra0-rx_crypts"`
UserRa0RxCrypts FlexInt `json:"user-ra0-rx_crypts"`
Ra0RxCrypts FlexInt `json:"ra0-rx_crypts"`
GuestRa0RxFrags FlexInt `json:"guest-ra0-rx_frags"`
UserRa0RxFrags FlexInt `json:"user-ra0-rx_frags"`
Ra0RxFrags FlexInt `json:"ra0-rx_frags"`
GuestRa0TxPackets FlexInt `json:"guest-ra0-tx_packets"`
UserRa0TxPackets FlexInt `json:"user-ra0-tx_packets"`
Ra0TxPackets FlexInt `json:"ra0-tx_packets"`
GuestRa0TxBytes FlexInt `json:"guest-ra0-tx_bytes"`
UserRa0TxBytes FlexInt `json:"user-ra0-tx_bytes"`
Ra0TxBytes FlexInt `json:"ra0-tx_bytes"`
GuestRa0TxErrors FlexInt `json:"guest-ra0-tx_errors"`
UserRa0TxErrors FlexInt `json:"user-ra0-tx_errors"`
Ra0TxErrors FlexInt `json:"ra0-tx_errors"`
GuestRa0TxDropped FlexInt `json:"guest-ra0-tx_dropped"`
UserRa0TxDropped FlexInt `json:"user-ra0-tx_dropped"`
Ra0TxDropped FlexInt `json:"ra0-tx_dropped"`
GuestRa0TxRetries FlexInt `json:"guest-ra0-tx_retries"`
UserRa0TxRetries FlexInt `json:"user-ra0-tx_retries"`
Ra0TxRetries FlexInt `json:"ra0-tx_retries"`
GuestRa0MacFilterRejections FlexInt `json:"guest-ra0-mac_filter_rejections"`
UserRa0MacFilterRejections FlexInt `json:"user-ra0-mac_filter_rejections"`
Ra0MacFilterRejections FlexInt `json:"ra0-mac_filter_rejections"`
GuestRa0WifiTxAttempts FlexInt `json:"guest-ra0-wifi_tx_attempts"`
UserRa0WifiTxAttempts FlexInt `json:"user-ra0-wifi_tx_attempts"`
Ra0WifiTxAttempts FlexInt `json:"ra0-wifi_tx_attempts"`
GuestRa0WifiTxDropped FlexInt `json:"guest-ra0-wifi_tx_dropped"`
UserRa0WifiTxDropped FlexInt `json:"user-ra0-wifi_tx_dropped"`
Ra0WifiTxDropped FlexInt `json:"ra0-wifi_tx_dropped"`
GuestRai0RxPackets FlexInt `json:"guest-rai0-rx_packets"`
UserRai0RxPackets FlexInt `json:"user-rai0-rx_packets"`
Rai0RxPackets FlexInt `json:"rai0-rx_packets"`
GuestRai0RxBytes FlexInt `json:"guest-rai0-rx_bytes"`
UserRai0RxBytes FlexInt `json:"user-rai0-rx_bytes"`
Rai0RxBytes FlexInt `json:"rai0-rx_bytes"`
GuestRai0RxErrors FlexInt `json:"guest-rai0-rx_errors"`
UserRai0RxErrors FlexInt `json:"user-rai0-rx_errors"`
Rai0RxErrors FlexInt `json:"rai0-rx_errors"`
GuestRai0RxDropped FlexInt `json:"guest-rai0-rx_dropped"`
UserRai0RxDropped FlexInt `json:"user-rai0-rx_dropped"`
Rai0RxDropped FlexInt `json:"rai0-rx_dropped"`
GuestRai0RxCrypts FlexInt `json:"guest-rai0-rx_crypts"`
UserRai0RxCrypts FlexInt `json:"user-rai0-rx_crypts"`
Rai0RxCrypts FlexInt `json:"rai0-rx_crypts"`
GuestRai0RxFrags FlexInt `json:"guest-rai0-rx_frags"`
UserRai0RxFrags FlexInt `json:"user-rai0-rx_frags"`
Rai0RxFrags FlexInt `json:"rai0-rx_frags"`
GuestRai0TxPackets FlexInt `json:"guest-rai0-tx_packets"`
UserRai0TxPackets FlexInt `json:"user-rai0-tx_packets"`
Rai0TxPackets FlexInt `json:"rai0-tx_packets"`
GuestRai0TxBytes FlexInt `json:"guest-rai0-tx_bytes"`
UserRai0TxBytes FlexInt `json:"user-rai0-tx_bytes"`
Rai0TxBytes FlexInt `json:"rai0-tx_bytes"`
GuestRai0TxErrors FlexInt `json:"guest-rai0-tx_errors"`
UserRai0TxErrors FlexInt `json:"user-rai0-tx_errors"`
Rai0TxErrors FlexInt `json:"rai0-tx_errors"`
GuestRai0TxDropped FlexInt `json:"guest-rai0-tx_dropped"`
UserRai0TxDropped FlexInt `json:"user-rai0-tx_dropped"`
Rai0TxDropped FlexInt `json:"rai0-tx_dropped"`
GuestRai0TxRetries FlexInt `json:"guest-rai0-tx_retries"`
UserRai0TxRetries FlexInt `json:"user-rai0-tx_retries"`
Rai0TxRetries FlexInt `json:"rai0-tx_retries"`
GuestRai0MacFilterRejections FlexInt `json:"guest-rai0-mac_filter_rejections"`
UserRai0MacFilterRejections FlexInt `json:"user-rai0-mac_filter_rejections"`
Rai0MacFilterRejections FlexInt `json:"rai0-mac_filter_rejections"`
GuestRai0WifiTxAttempts FlexInt `json:"guest-rai0-wifi_tx_attempts"`
UserRai0WifiTxAttempts FlexInt `json:"user-rai0-wifi_tx_attempts"`
Rai0WifiTxAttempts FlexInt `json:"rai0-wifi_tx_attempts"`
GuestRai0WifiTxDropped FlexInt `json:"guest-rai0-wifi_tx_dropped"`
UserRai0WifiTxDropped FlexInt `json:"user-rai0-wifi_tx_dropped"`
Rai0WifiTxDropped FlexInt `json:"rai0-wifi_tx_dropped"`
*/
}
// RadioTable is part of the data for UAPs and UDMs.
type RadioTable []struct {
AntennaGain FlexInt `json:"antenna_gain"`
BuiltinAntGain FlexInt `json:"builtin_ant_gain"`
BuiltinAntenna FlexBool `json:"builtin_antenna"`
Channel FlexInt `json:"channel"`
CurrentAntennaGain FlexInt `json:"current_antenna_gain"`
HasDfs FlexBool `json:"has_dfs"`
HasFccdfs FlexBool `json:"has_fccdfs"`
HasHt160 FlexBool `json:"has_ht160"`
Ht FlexInt `json:"ht"`
Is11Ac FlexBool `json:"is_11ac"`
MaxTxpower FlexInt `json:"max_txpower"`
MinRssi FlexInt `json:"min_rssi,omitempty"`
MinRssiEnabled FlexBool `json:"min_rssi_enabled"`
MinTxpower FlexInt `json:"min_txpower"`
Name string `json:"name"`
Nss FlexInt `json:"nss"`
Radio string `json:"radio"`
RadioCaps FlexInt `json:"radio_caps"`
SensLevelEnabled FlexBool `json:"sens_level_enabled"`
TxPower FlexInt `json:"tx_power"`
TxPowerMode string `json:"tx_power_mode"`
VwireEnabled FlexBool `json:"vwire_enabled"`
WlangroupID string `json:"wlangroup_id"`
}
// RadioTableStats is part of the data shared between UAP and UDM
type RadioTableStats []struct {
Name string `json:"name"`
Channel FlexInt `json:"channel"`
Radio string `json:"radio"`
AstTxto interface{} `json:"ast_txto"`
AstCst interface{} `json:"ast_cst"`
AstBeXmit FlexInt `json:"ast_be_xmit"`
CuTotal FlexInt `json:"cu_total"`
CuSelfRx FlexInt `json:"cu_self_rx"`
CuSelfTx FlexInt `json:"cu_self_tx"`
Gain FlexInt `json:"gain"`
Satisfaction FlexInt `json:"satisfaction"`
State string `json:"state"`
Extchannel FlexInt `json:"extchannel"`
TxPower FlexInt `json:"tx_power"`
TxPackets FlexInt `json:"tx_packets"`
TxRetries FlexInt `json:"tx_retries"`
NumSta FlexInt `json:"num_sta"`
GuestNumSta FlexInt `json:"guest-num_sta"`
UserNumSta FlexInt `json:"user-num_sta"`
}
// VapTable holds much of the UAP wireless data. Shared by UDM.
type VapTable []struct {
AnomaliesBarChart struct {
HighDNSLatency FlexInt `json:"high_dns_latency"`
HighTCPLatency FlexInt `json:"high_tcp_latency"`
HighTCPPacketLoss FlexInt `json:"high_tcp_packet_loss"`
HighWifiLatency FlexInt `json:"high_wifi_latency"`
HighWifiRetries FlexInt `json:"high_wifi_retries"`
LowPhyRate FlexInt `json:"low_phy_rate"`
PoorStreamEff FlexInt `json:"poor_stream_eff"`
SleepyClient FlexInt `json:"sleepy_client"`
StaArpTimeout FlexInt `json:"sta_arp_timeout"`
StaDNSTimeout FlexInt `json:"sta_dns_timeout"`
StaIPTimeout FlexInt `json:"sta_ip_timeout"`
WeakSignal FlexInt `json:"weak_signal"`
} `json:"anomalies_bar_chart"`
AnomaliesBarChartNow struct {
HighDNSLatency FlexInt `json:"high_dns_latency"`
HighTCPLatency FlexInt `json:"high_tcp_latency"`
HighTCPPacketLoss FlexInt `json:"high_tcp_packet_loss"`
HighWifiLatency FlexInt `json:"high_wifi_latency"`
HighWifiRetries FlexInt `json:"high_wifi_retries"`
LowPhyRate FlexInt `json:"low_phy_rate"`
PoorStreamEff FlexInt `json:"poor_stream_eff"`
SleepyClient FlexInt `json:"sleepy_client"`
StaArpTimeout FlexInt `json:"sta_arp_timeout"`
StaDNSTimeout FlexInt `json:"sta_dns_timeout"`
StaIPTimeout FlexInt `json:"sta_ip_timeout"`
WeakSignal FlexInt `json:"weak_signal"`
} `json:"anomalies_bar_chart_now"`
ReasonsBarChart struct {
PhyRate FlexInt `json:"phy_rate"`
Signal FlexInt `json:"signal"`
SleepyClient FlexInt `json:"sleepy_client"`
StaArpTimeout FlexInt `json:"sta_arp_timeout"`
StaDNSLatency FlexInt `json:"sta_dns_latency"`
StaDNSTimeout FlexInt `json:"sta_dns_timeout"`
StaIPTimeout FlexInt `json:"sta_ip_timeout"`
StreamEff FlexInt `json:"stream_eff"`
TCPLatency FlexInt `json:"tcp_latency"`
TCPPacketLoss FlexInt `json:"tcp_packet_loss"`
WifiLatency FlexInt `json:"wifi_latency"`
WifiRetries FlexInt `json:"wifi_retries"`
} `json:"reasons_bar_chart"`
ReasonsBarChartNow struct {
PhyRate FlexInt `json:"phy_rate"`
Signal FlexInt `json:"signal"`
SleepyClient FlexInt `json:"sleepy_client"`
StaArpTimeout FlexInt `json:"sta_arp_timeout"`
StaDNSLatency FlexInt `json:"sta_dns_latency"`
StaDNSTimeout FlexInt `json:"sta_dns_timeout"`
StaIPTimeout FlexInt `json:"sta_ip_timeout"`
StreamEff FlexInt `json:"stream_eff"`
TCPLatency FlexInt `json:"tcp_latency"`
TCPPacketLoss FlexInt `json:"tcp_packet_loss"`
WifiLatency FlexInt `json:"wifi_latency"`
WifiRetries FlexInt `json:"wifi_retries"`
} `json:"reasons_bar_chart_now"`
RxTCPStats struct {
Goodbytes FlexInt `json:"goodbytes"`
LatAvg FlexInt `json:"lat_avg"`
LatMax FlexInt `json:"lat_max"`
LatMin FlexInt `json:"lat_min"`
Stalls FlexInt `json:"stalls"`
} `json:"rx_tcp_stats"`
TxTCPStats struct {
Goodbytes FlexInt `json:"goodbytes"`
LatAvg FlexInt `json:"lat_avg"`
LatMax FlexInt `json:"lat_max"`
LatMin FlexInt `json:"lat_min"`
Stalls FlexInt `json:"stalls"`
} `json:"tx_tcp_stats"`
WifiTxLatencyMov struct {
Avg FlexInt `json:"avg"`
Max FlexInt `json:"max"`
Min FlexInt `json:"min"`
Total FlexInt `json:"total"`
TotalCount FlexInt `json:"total_count"`
} `json:"wifi_tx_latency_mov"`
ApMac string `json:"ap_mac"`
AvgClientSignal FlexInt `json:"avg_client_signal"`
Bssid string `json:"bssid"`
Ccq int `json:"ccq"`
Channel FlexInt `json:"channel"`
DNSAvgLatency FlexInt `json:"dns_avg_latency"`
Essid string `json:"essid"`
Extchannel int `json:"extchannel"`
ID string `json:"id"`
IsGuest FlexBool `json:"is_guest"`
IsWep FlexBool `json:"is_wep"`
MacFilterRejections int `json:"mac_filter_rejections"`
MapID interface{} `json:"map_id"`
Name string `json:"name"`
NumSatisfactionSta FlexInt `json:"num_satisfaction_sta"`
NumSta int `json:"num_sta"`
Radio string `json:"radio"`
RadioName string `json:"radio_name"`
RxBytes FlexInt `json:"rx_bytes"`
RxCrypts FlexInt `json:"rx_crypts"`
RxDropped FlexInt `json:"rx_dropped"`
RxErrors FlexInt `json:"rx_errors"`
RxFrags FlexInt `json:"rx_frags"`
RxNwids FlexInt `json:"rx_nwids"`
RxPackets FlexInt `json:"rx_packets"`
Satisfaction FlexInt `json:"satisfaction"`
SatisfactionNow FlexInt `json:"satisfaction_now"`
SiteID string `json:"site_id"`
State string `json:"state"`
T string `json:"t"`
TxBytes FlexInt `json:"tx_bytes"`
TxCombinedRetries FlexInt `json:"tx_combined_retries"`
TxDataMpduBytes FlexInt `json:"tx_data_mpdu_bytes"`
TxDropped FlexInt `json:"tx_dropped"`
TxErrors FlexInt `json:"tx_errors"`
TxPackets FlexInt `json:"tx_packets"`
TxPower FlexInt `json:"tx_power"`
TxRetries FlexInt `json:"tx_retries"`
TxRtsRetries FlexInt `json:"tx_rts_retries"`
TxSuccess FlexInt `json:"tx_success"`
TxTotal FlexInt `json:"tx_total"`
Up FlexBool `json:"up"`
Usage string `json:"usage"`
WifiTxAttempts FlexInt `json:"wifi_tx_attempts"`
WifiTxDropped FlexInt `json:"wifi_tx_dropped"`
WlanconfID string `json:"wlanconf_id"`
}
// UnmarshalJSON unmarshalls 5.10 or 5.11 formatted Access Point Stat data.
func (v *UAPStat) UnmarshalJSON(data []byte) error {
var n struct {
Ap `json:"ap"`
}
v.Ap = &n.Ap
err := json.Unmarshal(data, v.Ap) // controller version 5.10.
if err != nil {
return json.Unmarshal(data, &n) // controller version 5.11.
}
return nil
}

53
core/unifi/v4/uap_test.go Normal file
View File

@ -0,0 +1,53 @@
package unifi
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestUAPUnmarshalJSON(t *testing.T) {
testcontroller511 := `{
"ap": {
"site_id": "mySite",
"o": "ap",
"oid": "00:00:00:00:00:00",
"ap": "00:00:00:00:00:00",
"time": 1577742600000,
"datetime": "2019-12-30T09:50:00Z",
"user-wifi1-rx_packets": 6596670,
"user-wifi0-rx_packets": 42659527,
"user-rx_packets": 49294197,
"guest-rx_packets": 0,
"wifi0-rx_packets": 42639527,
"wifi1-rx_packets": 6591670,
"rx_packets": 49299197}}`
testcontroller510 := `{
"site_id": "mySite",
"o": "ap",
"oid": "00:00:00:00:00:00",
"ap": "00:00:00:00:00:00",
"time": 1577742600000,
"datetime": "2019-12-30T09:50:00Z",
"user-wifi1-rx_packets": 6596670,
"user-wifi0-rx_packets": 42659527,
"user-rx_packets": 49294197,
"guest-rx_packets": 0,
"wifi0-rx_packets": 42639527,
"wifi1-rx_packets": 6591670,
"rx_packets": 49299197}`
t.Parallel()
a := assert.New(t)
rxPakcets := 49299197
u := &UAPStat{}
err := u.UnmarshalJSON([]byte(testcontroller510))
a.Nil(err, "must be no error unmarshaling test strings")
a.Equal(float64(rxPakcets), u.RxPackets.Val, "data was not properly unmarshaled")
u = &UAPStat{} // reset
err = u.UnmarshalJSON([]byte(testcontroller511))
a.Nil(err, "must be no error unmarshaling test strings")
a.Equal(float64(rxPakcets), u.RxPackets.Val, "data was not properly unmarshaled")
}

183
core/unifi/v4/udm.go Normal file
View File

@ -0,0 +1,183 @@
package unifi
// UDM represents all the data from the Ubiquiti Controller for a Unifi Dream Machine.
// The UDM shares several structs/type-data with USW and USG.
type UDM struct {
SourceName string `json:"-"`
SiteID string `json:"site_id"`
SiteName string `json:"-"`
Mac string `json:"mac"`
Adopted FlexBool `json:"adopted"`
Serial string `json:"serial"`
IP string `json:"ip"`
Uptime FlexInt `json:"uptime"`
Model string `json:"model"`
Version string `json:"version"`
Name string `json:"name"`
Default FlexBool `json:"default"`
Locating FlexBool `json:"locating"`
Type string `json:"type"`
Unsupported FlexBool `json:"unsupported"`
UnsupportedReason FlexInt `json:"unsupported_reason"`
DiscoveredVia string `json:"discovered_via"`
AdoptIP string `json:"adopt_ip"`
AdoptURL string `json:"adopt_url"`
State FlexInt `json:"state"`
AdoptStatus FlexInt `json:"adopt_status"`
UpgradeState FlexInt `json:"upgrade_state"`
LastSeen FlexInt `json:"last_seen"`
AdoptableWhenUpgraded FlexBool `json:"adoptable_when_upgraded"`
Cfgversion string `json:"cfgversion"`
ConfigNetwork struct {
Type string `json:"type"`
IP string `json:"ip"`
} `json:"config_network"`
VwireTable []interface{} `json:"vwire_table"`
Dot1XPortctrlEnabled FlexBool `json:"dot1x_portctrl_enabled"`
JumboframeEnabled FlexBool `json:"jumboframe_enabled"`
FlowctrlEnabled FlexBool `json:"flowctrl_enabled"`
StpVersion string `json:"stp_version"`
StpPriority FlexInt `json:"stp_priority"`
PowerSourceCtrlEnabled FlexBool `json:"power_source_ctrl_enabled"`
LicenseState string `json:"license_state"`
ID string `json:"_id"`
DeviceID string `json:"device_id"`
AdoptState FlexInt `json:"adopt_state"`
AdoptTries FlexInt `json:"adopt_tries"`
AdoptManual FlexBool `json:"adopt_manual"`
InformURL string `json:"inform_url"`
InformIP string `json:"inform_ip"`
RequiredVersion string `json:"required_version"`
BoardRev FlexInt `json:"board_rev"`
EthernetTable []struct {
Mac string `json:"mac"`
NumPort FlexInt `json:"num_port"`
Name string `json:"name"`
} `json:"ethernet_table"`
PortTable []Port `json:"port_table"`
EthernetOverrides []struct {
Ifname string `json:"ifname"`
Networkgroup string `json:"networkgroup"`
} `json:"ethernet_overrides"`
UsgCaps FlexInt `json:"usg_caps"`
HasSpeaker FlexBool `json:"has_speaker"`
HasEth1 FlexBool `json:"has_eth1"`
FwCaps FlexInt `json:"fw_caps"`
HwCaps FlexInt `json:"hw_caps"`
WifiCaps FlexInt `json:"wifi_caps"`
SwitchCaps struct {
MaxMirrorSessions FlexInt `json:"max_mirror_sessions"`
MaxAggregateSessions FlexInt `json:"max_aggregate_sessions"`
} `json:"switch_caps"`
HasFan FlexBool `json:"has_fan"`
HasTemperature FlexBool `json:"has_temperature"`
RulesetInterfaces interface{} `json:"ruleset_interfaces"`
/* struct {
Br0 string `json:"br0"`
Eth0 string `json:"eth0"`
Eth1 string `json:"eth1"`
Eth2 string `json:"eth2"`
Eth3 string `json:"eth3"`
Eth4 string `json:"eth4"`
Eth5 string `json:"eth5"`
Eth6 string `json:"eth6"`
Eth7 string `json:"eth7"`
Eth8 string `json:"eth8"`
} */
KnownCfgversion string `json:"known_cfgversion"`
SysStats SysStats `json:"sys_stats"`
SystemStats SystemStats `json:"system-stats"`
GuestToken string `json:"guest_token"`
Overheating FlexBool `json:"overheating"`
SpeedtestStatus SpeedtestStatus `json:"speedtest-status"`
SpeedtestStatusSaved FlexBool `json:"speedtest-status-saved"`
Wan1 Wan `json:"wan1"`
Wan2 Wan `json:"wan2"`
Uplink Uplink `json:"uplink"`
ConnectRequestIP string `json:"connect_request_ip"`
ConnectRequestPort string `json:"connect_request_port"`
DownlinkTable []interface{} `json:"downlink_table"`
WlangroupIDNa string `json:"wlangroup_id_na"`
WlangroupIDNg string `json:"wlangroup_id_ng"`
BandsteeringMode string `json:"bandsteering_mode"`
RadioTable *RadioTable `json:"radio_table,omitempty"`
RadioTableStats *RadioTableStats `json:"radio_table_stats,omitempty"`
VapTable *VapTable `json:"vap_table"`
XInformAuthkey string `json:"x_inform_authkey"`
NetworkTable NetworkTable `json:"network_table"`
PortOverrides []struct {
PortIdx FlexInt `json:"port_idx"`
PortconfID string `json:"portconf_id"`
} `json:"port_overrides"`
Stat UDMStat `json:"stat"`
TxBytes FlexInt `json:"tx_bytes"`
RxBytes FlexInt `json:"rx_bytes"`
Bytes FlexInt `json:"bytes"`
BytesD FlexInt `json:"bytes-d"`
TxBytesD FlexInt `json:"tx_bytes-d"`
RxBytesD FlexInt `json:"rx_bytes-d"`
BytesR FlexInt `json:"bytes-r"`
NumSta FlexInt `json:"num_sta"` // USG
WlanNumSta FlexInt `json:"wlan-num_sta"` // UAP
LanNumSta FlexInt `json:"lan-num_sta"` // USW
UserWlanNumSta FlexInt `json:"user-wlan-num_sta"` // UAP
UserLanNumSta FlexInt `json:"user-lan-num_sta"` // USW
UserNumSta FlexInt `json:"user-num_sta"` // USG
GuestWlanNumSta FlexInt `json:"guest-wlan-num_sta"` // UAP
GuestLanNumSta FlexInt `json:"guest-lan-num_sta"` // USW
GuestNumSta FlexInt `json:"guest-num_sta"` // USG
NumDesktop FlexInt `json:"num_desktop"` // USG
NumMobile FlexInt `json:"num_mobile"` // USG
NumHandheld FlexInt `json:"num_handheld"` // USG
}
// NetworkTable is the list of networks on a gateway.
type NetworkTable []struct {
ID string `json:"_id"`
AttrNoDelete FlexBool `json:"attr_no_delete"`
AttrHiddenID string `json:"attr_hidden_id"`
Name string `json:"name"`
SiteID string `json:"site_id"`
VlanEnabled FlexBool `json:"vlan_enabled"`
Purpose string `json:"purpose"`
IPSubnet string `json:"ip_subnet"`
Ipv6InterfaceType string `json:"ipv6_interface_type"`
DomainName string `json:"domain_name"`
IsNat FlexBool `json:"is_nat"`
DhcpdEnabled FlexBool `json:"dhcpd_enabled"`
DhcpdStart string `json:"dhcpd_start"`
DhcpdStop string `json:"dhcpd_stop"`
Dhcpdv6Enabled FlexBool `json:"dhcpdv6_enabled"`
Ipv6RaEnabled FlexBool `json:"ipv6_ra_enabled"`
LteLanEnabled FlexBool `json:"lte_lan_enabled"`
Networkgroup string `json:"networkgroup"`
DhcpdLeasetime FlexInt `json:"dhcpd_leasetime"`
DhcpdDNSEnabled FlexBool `json:"dhcpd_dns_enabled"`
DhcpdGatewayEnabled FlexBool `json:"dhcpd_gateway_enabled"`
DhcpdTimeOffsetEnabled FlexBool `json:"dhcpd_time_offset_enabled"`
Ipv6PdStart string `json:"ipv6_pd_start"`
Ipv6PdStop string `json:"ipv6_pd_stop"`
DhcpdDNS1 string `json:"dhcpd_dns_1"`
DhcpdDNS2 string `json:"dhcpd_dns_2"`
DhcpdDNS3 string `json:"dhcpd_dns_3"`
DhcpdDNS4 string `json:"dhcpd_dns_4"`
Enabled FlexBool `json:"enabled"`
DhcpRelayEnabled FlexBool `json:"dhcp_relay_enabled"`
Mac string `json:"mac"`
IsGuest FlexBool `json:"is_guest"`
IP string `json:"ip"`
Up FlexBool `json:"up"`
NumSta FlexInt `json:"num_sta"`
RxBytes FlexInt `json:"rx_bytes"`
RxPackets FlexInt `json:"rx_packets"`
TxBytes FlexInt `json:"tx_bytes"`
TxPackets FlexInt `json:"tx_packets"`
}
// UDMStat holds the "stat" data for a dream machine.
// A dream machine is a USG + USW + Controller
type UDMStat struct {
*Gw `json:"gw"`
*Sw `json:"sw"`
*Ap `json:"ap,omitempty"`
}

160
core/unifi/v4/unifi.go Normal file
View File

@ -0,0 +1,160 @@
// Package unifi provides a set of types to unload (unmarshal) Ubiquiti UniFi
// controller data. Also provided are methods to easily get data for devices -
// things like access points and switches, and for clients - the things
// connected to those access points and switches. As a bonus, each device and
// client type provided has an attached method to create InfluxDB datapoints.
package unifi
import (
"bytes"
"crypto/tls"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net/http"
"net/http/cookiejar"
"strings"
"time"
)
// NewUnifi creates a http.Client with authenticated cookies.
// Used to make additional, authenticated requests to the APIs.
// Start here.
func NewUnifi(config *Config) (*Unifi, error) {
jar, err := cookiejar.New(nil)
if err != nil {
return nil, err
}
config.URL = strings.TrimRight(config.URL, "/")
if config.ErrorLog == nil {
config.ErrorLog = discardLogs
}
if config.DebugLog == nil {
config.DebugLog = discardLogs
}
u := &Unifi{Config: config,
Client: &http.Client{
Jar: jar,
Transport: &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: !config.VerifySSL},
},
},
}
if err := u.Login(); err != nil {
return u, err
}
if err := u.GetServerData(); err != nil {
return u, fmt.Errorf("unable to get server version: %v", err)
}
return u, nil
}
// Login is a helper method. It can be called to grab a new authentication cookie.
func (u *Unifi) Login() error {
start := time.Now()
// magic login.
req, err := u.UniReq(APILoginPath, fmt.Sprintf(`{"username":"%s","password":"%s"}`, u.User, u.Pass))
if err != nil {
return err
}
resp, err := u.Do(req)
if err != nil {
return err
}
defer resp.Body.Close() // we need no data here.
_, _ = io.Copy(ioutil.Discard, resp.Body) // avoid leaking.
u.DebugLog("Requested %s: elapsed %v, returned %d bytes",
APILoginPath, time.Since(start).Round(time.Millisecond), resp.ContentLength)
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("authentication failed (user: %s): %s (status: %s)",
u.User, u.URL+APILoginPath, resp.Status)
}
return nil
}
// GetServerData sets the controller's version and UUID. Only call this if you
// previously called Login and suspect the controller version has changed.
func (u *Unifi) GetServerData() error {
var response struct {
Data server `json:"meta"`
}
u.server = &response.Data
return u.GetData(APIStatusPath, &response)
}
// GetData makes a unifi request and unmarshals the response into a provided pointer.
func (u *Unifi) GetData(apiPath string, v interface{}, params ...string) error {
start := time.Now()
body, err := u.GetJSON(apiPath, params...)
if err != nil {
return err
}
u.DebugLog("Requested %s: elapsed %v, returned %d bytes",
apiPath, time.Since(start).Round(time.Millisecond), len(body))
return json.Unmarshal(body, v)
}
// UniReq is a small helper function that adds an Accept header.
// Use this if you're unmarshalling UniFi data into custom types.
// And if you're doing that... sumbut a pull request with your new struct. :)
// This is a helper method that is exposed for convenience.
func (u *Unifi) UniReq(apiPath string, params string) (req *http.Request, err error) {
switch params {
case "":
req, err = http.NewRequest("GET", u.URL+apiPath, nil)
default:
req, err = http.NewRequest("POST", u.URL+apiPath, bytes.NewBufferString(params))
}
if err != nil {
return
}
req.Header.Add("Accept", "application/json")
u.DebugLog("Requesting %s, with params: %v", apiPath, params != "")
return
}
// GetJSON returns the raw JSON from a path. This is useful for debugging.
func (u *Unifi) GetJSON(apiPath string, params ...string) ([]byte, error) {
req, err := u.UniReq(apiPath, strings.Join(params, " "))
if err != nil {
return []byte{}, err
}
resp, err := u.Do(req)
if err != nil {
return []byte{}, err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return body, err
}
if resp.StatusCode != http.StatusOK {
err = fmt.Errorf("invalid status code from server %s", resp.Status)
}
return body, err
}

View File

@ -0,0 +1,62 @@
package unifi
import (
"io/ioutil"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
)
func TestNewUnifi(t *testing.T) {
t.Parallel()
a := assert.New(t)
u := "http://127.0.0.1:64431"
c := &Config{
User: "user1",
Pass: "pass2",
URL: u,
VerifySSL: false,
DebugLog: discardLogs,
}
authReq, err := NewUnifi(c)
a.NotNil(err)
a.EqualValues(u, authReq.URL)
a.Contains(err.Error(), "connection refused", "an invalid destination should produce a connection error.")
}
func TestUniReq(t *testing.T) {
t.Parallel()
a := assert.New(t)
p := "/test/path"
u := "http://some.url:8443"
// Test empty parameters.
authReq := &Unifi{Client: &http.Client{}, Config: &Config{URL: u, DebugLog: discardLogs}}
r, err := authReq.UniReq(p, "")
a.Nil(err, "newrequest must not produce an error")
a.EqualValues(p, r.URL.Path,
"the provided apiPath was not added to http request")
a.EqualValues(u, r.URL.Scheme+"://"+r.URL.Host, "URL improperly encoded")
a.EqualValues("GET", r.Method, "without parameters the method must be GET")
a.EqualValues("application/json", r.Header.Get("Accept"), "Accept header must be set to application/json")
// Test with parameters
k := "key1=value9&key2=value7"
authReq = &Unifi{Client: &http.Client{}, Config: &Config{URL: "http://some.url:8443", DebugLog: discardLogs}}
r, err = authReq.UniReq(p, k)
a.Nil(err, "newrequest must not produce an error")
a.EqualValues(p, r.URL.Path,
"the provided apiPath was not added to http request")
a.EqualValues(u, r.URL.Scheme+"://"+r.URL.Host, "URL improperly encoded")
a.EqualValues("POST", r.Method, "with parameters the method must be POST")
a.EqualValues("application/json", r.Header.Get("Accept"), "Accept header must be set to application/json")
// Check the parameters.
d, err := ioutil.ReadAll(r.Body)
a.Nil(err, "problem reading request body, POST parameters may be malformed")
a.EqualValues(k, string(d), "POST parameters improperly encoded")
}
/* NOT DONE: OPEN web server, check parameters posted, more. This test is incomplete.
a.EqualValues(`{"username": "user1","password": "pass2"}`, string(post_params),
"user/pass json parameters improperly encoded")
*/

252
core/unifi/v4/usg.go Normal file
View File

@ -0,0 +1,252 @@
package unifi
import (
"encoding/json"
"time"
)
// USG represents all the data from the Ubiquiti Controller for a Unifi Security Gateway.
type USG struct {
SourceName string `json:"-"`
ID string `json:"_id"`
Adopted FlexBool `json:"adopted"`
Cfgversion string `json:"cfgversion"`
ConfigNetwork struct {
Type string `json:"type"`
IP string `json:"ip"`
} `json:"config_network"`
EthernetTable []struct {
Mac string `json:"mac"`
NumPort FlexInt `json:"num_port"`
Name string `json:"name"`
} `json:"ethernet_table"`
FwCaps FlexInt `json:"fw_caps"`
InformIP string `json:"inform_ip"`
InformURL string `json:"inform_url"`
IP string `json:"ip"`
LedOverride string `json:"led_override"`
LicenseState string `json:"license_state"`
Mac string `json:"mac"`
Model string `json:"model"`
Name string `json:"name"`
OutdoorModeOverride string `json:"outdoor_mode_override"`
Serial string `json:"serial"`
SiteID string `json:"site_id"`
SiteName string `json:"-"`
Type string `json:"type"`
UsgCaps FlexInt `json:"usg_caps"`
Version string `json:"version"`
RequiredVersion string `json:"required_version"`
EthernetOverrides []struct {
Ifname string `json:"ifname"`
Networkgroup string `json:"networkgroup"`
} `json:"ethernet_overrides"`
HwCaps FlexInt `json:"hw_caps"`
BoardRev FlexInt `json:"board_rev"`
Unsupported FlexBool `json:"unsupported"`
UnsupportedReason FlexInt `json:"unsupported_reason"`
DeviceID string `json:"device_id"`
State FlexInt `json:"state"`
LastSeen FlexInt `json:"last_seen"`
Upgradable FlexBool `json:"upgradable"`
AdoptableWhenUpgraded FlexBool `json:"adoptable_when_upgraded"`
Rollupgrade FlexBool `json:"rollupgrade"`
KnownCfgversion string `json:"known_cfgversion"`
Uptime FlexInt `json:"uptime"`
Locating FlexBool `json:"locating"`
ConnectRequestIP string `json:"connect_request_ip"`
ConnectRequestPort string `json:"connect_request_port"`
SysStats SysStats `json:"sys_stats"`
SystemStats SystemStats `json:"system-stats"`
GuestToken string `json:"guest_token"`
SpeedtestStatus SpeedtestStatus `json:"speedtest-status"`
SpeedtestStatusSaved FlexBool `json:"speedtest-status-saved"`
Wan1 Wan `json:"wan1"`
Wan2 Wan `json:"wan2"`
PortTable []struct {
Name string `json:"name"`
Ifname string `json:"ifname"`
IP string `json:"ip"`
Netmask string `json:"netmask"`
Mac string `json:"mac"`
Up FlexBool `json:"up"`
Speed FlexInt `json:"speed"`
FullDuplex FlexBool `json:"full_duplex"`
RxBytes FlexInt `json:"rx_bytes"`
RxDropped FlexInt `json:"rx_dropped"`
RxErrors FlexInt `json:"rx_errors"`
RxPackets FlexInt `json:"rx_packets"`
TxBytes FlexInt `json:"tx_bytes"`
TxDropped FlexInt `json:"tx_dropped"`
TxErrors FlexInt `json:"tx_errors"`
TxPackets FlexInt `json:"tx_packets"`
RxMulticast FlexInt `json:"rx_multicast"`
Enable FlexBool `json:"enable"`
DNS []string `json:"dns,omitempty"`
Gateway string `json:"gateway,omitempty"`
} `json:"port_table"`
NetworkTable NetworkTable `json:"network_table"`
Uplink Uplink `json:"uplink"`
Stat USGStat `json:"stat"`
TxBytes FlexInt `json:"tx_bytes"`
RxBytes FlexInt `json:"rx_bytes"`
Bytes FlexInt `json:"bytes"`
NumSta FlexInt `json:"num_sta"`
UserNumSta FlexInt `json:"user-num_sta"`
GuestNumSta FlexInt `json:"guest-num_sta"`
NumDesktop FlexInt `json:"num_desktop"`
NumMobile FlexInt `json:"num_mobile"`
NumHandheld FlexInt `json:"num_handheld"`
}
// Uplink is the Internet connection (or uplink) on a UniFi device.
type Uplink struct {
BytesR FlexInt `json:"bytes-r"`
Drops FlexInt `json:"drops"`
Enable FlexBool `json:"enable,omitempty"`
FullDuplex FlexBool `json:"full_duplex"`
Gateways []string `json:"gateways,omitempty"`
IP string `json:"ip"`
Latency FlexInt `json:"latency"`
Mac string `json:"mac,omitempty"`
MaxSpeed FlexInt `json:"max_speed"`
Name string `json:"name"`
Nameservers []string `json:"nameservers"`
Netmask string `json:"netmask"`
NumPort FlexInt `json:"num_port"`
RxBytes FlexInt `json:"rx_bytes"`
RxBytesR FlexInt `json:"rx_bytes-r"`
RxDropped FlexInt `json:"rx_dropped"`
RxErrors FlexInt `json:"rx_errors"`
RxMulticast FlexInt `json:"rx_multicast"`
RxPackets FlexInt `json:"rx_packets"`
Speed FlexInt `json:"speed"`
SpeedtestLastrun FlexInt `json:"speedtest_lastrun,omitempty"`
SpeedtestPing FlexInt `json:"speedtest_ping,omitempty"`
SpeedtestStatus string `json:"speedtest_status,omitempty"`
TxBytes FlexInt `json:"tx_bytes"`
TxBytesR FlexInt `json:"tx_bytes-r"`
TxDropped FlexInt `json:"tx_dropped"`
TxErrors FlexInt `json:"tx_errors"`
TxPackets FlexInt `json:"tx_packets"`
Type string `json:"type"`
Up FlexBool `json:"up"`
Uptime FlexInt `json:"uptime"`
XputDown FlexInt `json:"xput_down,omitempty"`
XputUp FlexInt `json:"xput_up,omitempty"`
}
// Wan is a Wan interface on a USG or UDM.
type Wan struct {
Autoneg FlexBool `json:"autoneg"`
BytesR FlexInt `json:"bytes-r"`
DNS []string `json:"dns"`
Enable FlexBool `json:"enable"`
FlowctrlRx FlexBool `json:"flowctrl_rx"`
FlowctrlTx FlexBool `json:"flowctrl_tx"`
FullDuplex FlexBool `json:"full_duplex"`
Gateway string `json:"gateway"` // may be deprecated
IP string `json:"ip"`
Ifname string `json:"ifname"`
IsUplink FlexBool `json:"is_uplink"`
Mac string `json:"mac"`
MaxSpeed FlexInt `json:"max_speed"`
Media string `json:"media"`
Name string `json:"name"`
Netmask string `json:"netmask"` // may be deprecated
NumPort int `json:"num_port"`
PortIdx int `json:"port_idx"`
PortPoe FlexBool `json:"port_poe"`
RxBroadcast FlexInt `json:"rx_broadcast"`
RxBytes FlexInt `json:"rx_bytes"`
RxBytesR FlexInt `json:"rx_bytes-r"`
RxDropped FlexInt `json:"rx_dropped"`
RxErrors FlexInt `json:"rx_errors"`
RxMulticast FlexInt `json:"rx_multicast"`
RxPackets FlexInt `json:"rx_packets"`
Speed FlexInt `json:"speed"`
TxBroadcast FlexInt `json:"tx_broadcast"`
TxBytes FlexInt `json:"tx_bytes"`
TxBytesR FlexInt `json:"tx_bytes-r"`
TxDropped FlexInt `json:"tx_dropped"`
TxErrors FlexInt `json:"tx_errors"`
TxMulticast FlexInt `json:"tx_multicast"`
TxPackets FlexInt `json:"tx_packets"`
Type string `json:"type"`
Up FlexBool `json:"up"`
}
// SpeedtestStatus is the speed test info on a USG or UDM.
type SpeedtestStatus struct {
Latency FlexInt `json:"latency"`
Rundate FlexInt `json:"rundate"`
Runtime FlexInt `json:"runtime"`
ServerDesc string `json:"server_desc,omitempty"`
StatusDownload FlexInt `json:"status_download"`
StatusPing FlexInt `json:"status_ping"`
StatusSummary FlexInt `json:"status_summary"`
StatusUpload FlexInt `json:"status_upload"`
XputDownload FlexInt `json:"xput_download"`
XputUpload FlexInt `json:"xput_upload"`
}
// SystemStats is system info for a UDM, USG, USW.
type SystemStats struct {
CPU FlexInt `json:"cpu"`
Mem FlexInt `json:"mem"`
Uptime FlexInt `json:"uptime"`
}
// SysStats is load info for a UDM, USG, USW.
type SysStats struct {
Loadavg1 FlexInt `json:"loadavg_1"`
Loadavg15 FlexInt `json:"loadavg_15"`
Loadavg5 FlexInt `json:"loadavg_5"`
MemBuffer FlexInt `json:"mem_buffer"`
MemTotal FlexInt `json:"mem_total"`
MemUsed FlexInt `json:"mem_used"`
}
// USGStat holds the "stat" data for a gateway.
// This is split out because of a JSON data format change from 5.10 to 5.11.
type USGStat struct {
*Gw
}
// Gw is a subtype of USGStat to make unmarshalling of different controller versions possible.
type Gw struct {
SiteID string `json:"site_id"`
O string `json:"o"`
Oid string `json:"oid"`
Gw string `json:"gw"`
Time FlexInt `json:"time"`
Datetime time.Time `json:"datetime"`
Duration FlexInt `json:"duration"`
WanRxPackets FlexInt `json:"wan-rx_packets"`
WanRxBytes FlexInt `json:"wan-rx_bytes"`
WanRxDropped FlexInt `json:"wan-rx_dropped"`
WanTxPackets FlexInt `json:"wan-tx_packets"`
WanTxBytes FlexInt `json:"wan-tx_bytes"`
LanRxPackets FlexInt `json:"lan-rx_packets"`
LanRxBytes FlexInt `json:"lan-rx_bytes"`
LanTxPackets FlexInt `json:"lan-tx_packets"`
LanTxBytes FlexInt `json:"lan-tx_bytes"`
LanRxDropped FlexInt `json:"lan-rx_dropped"`
WanRxErrors FlexInt `json:"wan-rx_errors,omitempty"`
}
// UnmarshalJSON unmarshalls 5.10 or 5.11 formatted Gateway Stat data.
func (v *USGStat) UnmarshalJSON(data []byte) error {
var n struct {
Gw `json:"gw"`
}
v.Gw = &n.Gw
err := json.Unmarshal(data, v.Gw) // controller version 5.10.
if err != nil {
return json.Unmarshal(data, &n) // controller version 5.11.
}
return nil
}

60
core/unifi/v4/usg_test.go Normal file
View File

@ -0,0 +1,60 @@
package unifi
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestUSGUnmarshalJSON(t *testing.T) {
testcontroller511 := `{
"gw": {
"site_id": "mySite",
"o": "gw",
"oid": "00:00:00:00:00:00",
"gw": "00:00:00:00:00:00",
"time": 1577742600000,
"datetime": "2019-12-30T09:50:00Z",
"bytes": 0,
"duration": 3590568000,
"wan-rx_packets": 299729434558,
"wan-rx_bytes": 299882768958208,
"wan-tx_packets": 249639259523,
"wan-tx_bytes": 169183252492369,
"lan-rx_packets": 78912349453,
"lan-rx_bytes": 37599596992669,
"lan-tx_packets": 12991234992,
"lan-tx_bytes": 11794664098210}}`
testcontroller510 := `{
"site_id": "mySite",
"o": "gw",
"oid": "00:00:00:00:00:00",
"gw": "00:00:00:00:00:00",
"time": 1577742600000,
"datetime": "2019-12-30T09:50:00Z",
"bytes": 0,
"duration": 3590568000,
"wan-rx_packets": 299729434558,
"wan-rx_bytes": 299882768958208,
"wan-tx_packets": 249639259523,
"wan-tx_bytes": 169183252492369,
"lan-rx_packets": 78912349453,
"lan-rx_bytes": 37599596992669,
"lan-tx_packets": 12991234992,
"lan-tx_bytes": 11794664098210}`
t.Parallel()
a := assert.New(t)
u := &USGStat{}
lanRx := 37599596992669
err := u.UnmarshalJSON([]byte(testcontroller510))
a.Nil(err, "must be no error unmarshaling test strings")
a.Equal(float64(lanRx), u.LanRxBytes.Val, "data was not properly unmarshaled")
u = &USGStat{} // reset
err = u.UnmarshalJSON([]byte(testcontroller511))
a.Nil(err, "must be no error unmarshaling test strings")
a.Equal(float64(lanRx), u.LanRxBytes.Val, "data was not properly unmarshaled")
}

377
core/unifi/v4/usw.go Normal file
View File

@ -0,0 +1,377 @@
package unifi
import (
"encoding/json"
"time"
)
// USW represents all the data from the Ubiquiti Controller for a Unifi Switch.
type USW struct {
SourceName string `json:"-"`
SiteName string `json:"-"`
ID string `json:"_id"`
Adopted FlexBool `json:"adopted"`
BoardRev FlexInt `json:"board_rev"`
Cfgversion string `json:"cfgversion"`
ConfigNetwork struct {
Type string `json:"type"`
IP string `json:"ip"`
} `json:"config_network"`
Dot1XPortctrlEnabled FlexBool `json:"dot1x_portctrl_enabled"`
EthernetTable []struct {
Mac string `json:"mac"`
NumPort FlexInt `json:"num_port,omitempty"`
Name string `json:"name"`
} `json:"ethernet_table"`
FlowctrlEnabled FlexBool `json:"flowctrl_enabled"`
FwCaps FlexInt `json:"fw_caps"`
HasFan FlexBool `json:"has_fan"`
HasTemperature FlexBool `json:"has_temperature"`
InformIP string `json:"inform_ip"`
InformURL string `json:"inform_url"`
IP string `json:"ip"`
JumboframeEnabled FlexBool `json:"jumboframe_enabled"`
LedOverride string `json:"led_override"`
LicenseState string `json:"license_state"`
Mac string `json:"mac"`
Model string `json:"model"`
Name string `json:"name"`
OutdoorModeOverride string `json:"outdoor_mode_override"`
PortOverrides []struct {
Name string `json:"name,omitempty"`
PoeMode string `json:"poe_mode,omitempty"`
PortIdx FlexInt `json:"port_idx"`
PortconfID string `json:"portconf_id"`
} `json:"port_overrides"`
PortTable []Port `json:"port_table"`
Serial string `json:"serial"`
SiteID string `json:"site_id"`
StpPriority FlexInt `json:"stp_priority"`
StpVersion string `json:"stp_version"`
Type string `json:"type"`
Version string `json:"version"`
RequiredVersion string `json:"required_version"`
SwitchCaps struct {
FeatureCaps FlexInt `json:"feature_caps"`
MaxMirrorSessions FlexInt `json:"max_mirror_sessions"`
MaxAggregateSessions FlexInt `json:"max_aggregate_sessions"`
} `json:"switch_caps"`
HwCaps FlexInt `json:"hw_caps"`
Unsupported FlexBool `json:"unsupported"`
UnsupportedReason FlexInt `json:"unsupported_reason"`
SysErrorCaps FlexInt `json:"sys_error_caps"`
DeviceID string `json:"device_id"`
State FlexInt `json:"state"`
LastSeen FlexInt `json:"last_seen"`
Upgradable FlexBool `json:"upgradable,omitempty"`
AdoptableWhenUpgraded FlexBool `json:"adoptable_when_upgraded,omitempty"`
Rollupgrade FlexBool `json:"rollupgrade,omitempty"`
KnownCfgversion string `json:"known_cfgversion"`
Uptime FlexInt `json:"uptime"`
Locating FlexBool `json:"locating"`
ConnectRequestIP string `json:"connect_request_ip"`
ConnectRequestPort string `json:"connect_request_port"`
SysStats SysStats `json:"sys_stats"`
SystemStats SystemStats `json:"system-stats"`
FanLevel FlexInt `json:"fan_level"`
GeneralTemperature FlexInt `json:"general_temperature"`
Overheating FlexBool `json:"overheating"`
TotalMaxPower FlexInt `json:"total_max_power"`
DownlinkTable []struct {
PortIdx FlexInt `json:"port_idx"`
Speed FlexInt `json:"speed"`
FullDuplex FlexBool `json:"full_duplex"`
Mac string `json:"mac"`
} `json:"downlink_table"`
Uplink Uplink `json:"uplink"`
LastUplink struct {
UplinkMac string `json:"uplink_mac"`
} `json:"last_uplink"`
UplinkDepth FlexInt `json:"uplink_depth"`
Stat USWStat `json:"stat"`
TxBytes FlexInt `json:"tx_bytes"`
RxBytes FlexInt `json:"rx_bytes"`
Bytes FlexInt `json:"bytes"`
NumSta FlexInt `json:"num_sta"`
UserNumSta FlexInt `json:"user-num_sta"`
GuestNumSta FlexInt `json:"guest-num_sta"`
}
// Port is a physical connection on a USW or UDM.
type Port struct {
AggregatedBy FlexBool `json:"aggregated_by"`
Autoneg FlexBool `json:"autoneg,omitempty"`
BytesR FlexInt `json:"bytes-r"`
DNS []string `json:"dns,omitempty"`
Dot1XMode string `json:"dot1x_mode"`
Dot1XStatus string `json:"dot1x_status"`
Enable FlexBool `json:"enable"`
FlowctrlRx FlexBool `json:"flowctrl_rx"`
FlowctrlTx FlexBool `json:"flowctrl_tx"`
FullDuplex FlexBool `json:"full_duplex"`
IP string `json:"ip,omitempty"`
Ifname string `json:"ifname,omitempty"`
IsUplink FlexBool `json:"is_uplink"`
Mac string `json:"mac,omitempty"`
Jumbo FlexBool `json:"jumbo,omitempty"`
Masked FlexBool `json:"masked"`
Media string `json:"media"`
Name string `json:"name"`
NetworkName string `json:"network_name,omitempty"`
NumPort int `json:"num_port,omitempty"`
OpMode string `json:"op_mode"`
PoeCaps FlexInt `json:"poe_caps"`
PoeClass string `json:"poe_class,omitempty"`
PoeCurrent FlexInt `json:"poe_current,omitempty"`
PoeEnable FlexBool `json:"poe_enable,omitempty"`
PoeGood FlexBool `json:"poe_good,omitempty"`
PoeMode string `json:"poe_mode,omitempty"`
PoePower FlexInt `json:"poe_power,omitempty"`
PoeVoltage FlexInt `json:"poe_voltage,omitempty"`
PortDelta struct {
TimeDelta int64 `json:"time_delta"`
} `json:"port_delta,omitempty"`
PortIdx FlexInt `json:"port_idx"`
PortPoe FlexBool `json:"port_poe"`
PortconfID string `json:"portconf_id"`
RxBroadcast FlexInt `json:"rx_broadcast"`
RxBytes FlexInt `json:"rx_bytes"`
RxBytesR FlexInt `json:"rx_bytes-r"`
RxDropped FlexInt `json:"rx_dropped"`
RxErrors FlexInt `json:"rx_errors"`
RxMulticast FlexInt `json:"rx_multicast"`
RxPackets FlexInt `json:"rx_packets"`
Satisfaction FlexInt `json:"satisfaction,omitempty"`
SfpFound FlexBool `json:"sfp_found,omitempty"`
Speed FlexInt `json:"speed"`
SpeedCaps FlexInt `json:"speed_caps"`
StpPathcost FlexInt `json:"stp_pathcost"`
StpState string `json:"stp_state"`
TxBroadcast FlexInt `json:"tx_broadcast"`
TxBytes FlexInt `json:"tx_bytes"`
TxBytesR FlexInt `json:"tx_bytes-r"`
TxDropped FlexInt `json:"tx_dropped"`
TxErrors FlexInt `json:"tx_errors"`
TxMulticast FlexInt `json:"tx_multicast"`
TxPackets FlexInt `json:"tx_packets"`
Type string `json:"type,omitempty"`
Up FlexBool `json:"up"`
}
// USWStat holds the "stat" data for a switch.
// This is split out because of a JSON data format change from 5.10 to 5.11.
type USWStat struct {
*Sw
}
// Sw is a subtype of USWStat to make unmarshalling of different controller versions possible.
type Sw struct {
SiteID string `json:"site_id"`
O string `json:"o"`
Oid string `json:"oid"`
Sw string `json:"sw"`
Time FlexInt `json:"time"`
Datetime time.Time `json:"datetime"`
RxPackets FlexInt `json:"rx_packets"`
RxBytes FlexInt `json:"rx_bytes"`
RxErrors FlexInt `json:"rx_errors"`
RxDropped FlexInt `json:"rx_dropped"`
RxCrypts FlexInt `json:"rx_crypts"`
RxFrags FlexInt `json:"rx_frags"`
TxPackets FlexInt `json:"tx_packets"`
TxBytes FlexInt `json:"tx_bytes"`
TxErrors FlexInt `json:"tx_errors"`
TxDropped FlexInt `json:"tx_dropped"`
TxRetries FlexInt `json:"tx_retries"`
RxMulticast FlexInt `json:"rx_multicast"`
RxBroadcast FlexInt `json:"rx_broadcast"`
TxMulticast FlexInt `json:"tx_multicast"`
TxBroadcast FlexInt `json:"tx_broadcast"`
Bytes FlexInt `json:"bytes"`
Duration FlexInt `json:"duration"`
/* These are all in port table */
/*
Port1RxPackets FlexInt `json:"port_1-rx_packets,omitempty"`
Port1RxBytes FlexInt `json:"port_1-rx_bytes,omitempty"`
Port1TxPackets FlexInt `json:"port_1-tx_packets,omitempty"`
Port1TxBytes FlexInt `json:"port_1-tx_bytes,omitempty"`
Port1TxMulticast FlexInt `json:"port_1-tx_multicast"`
Port1TxBroadcast FlexInt `json:"port_1-tx_broadcast"`
Port3RxPackets FlexInt `json:"port_3-rx_packets,omitempty"`
Port3RxBytes FlexInt `json:"port_3-rx_bytes,omitempty"`
Port3TxPackets FlexInt `json:"port_3-tx_packets,omitempty"`
Port3TxBytes FlexInt `json:"port_3-tx_bytes,omitempty"`
Port3RxBroadcast FlexInt `json:"port_3-rx_broadcast"`
Port3TxMulticast FlexInt `json:"port_3-tx_multicast"`
Port3TxBroadcast FlexInt `json:"port_3-tx_broadcast"`
Port6RxPackets FlexInt `json:"port_6-rx_packets,omitempty"`
Port6RxBytes FlexInt `json:"port_6-rx_bytes,omitempty"`
Port6TxPackets FlexInt `json:"port_6-tx_packets,omitempty"`
Port6TxBytes FlexInt `json:"port_6-tx_bytes,omitempty"`
Port6RxMulticast FlexInt `json:"port_6-rx_multicast"`
Port6TxMulticast FlexInt `json:"port_6-tx_multicast"`
Port6TxBroadcast FlexInt `json:"port_6-tx_broadcast"`
Port7RxPackets FlexInt `json:"port_7-rx_packets,omitempty"`
Port7RxBytes FlexInt `json:"port_7-rx_bytes,omitempty"`
Port7TxPackets FlexInt `json:"port_7-tx_packets,omitempty"`
Port7TxBytes FlexInt `json:"port_7-tx_bytes,omitempty"`
Port7TxMulticast FlexInt `json:"port_7-tx_multicast"`
Port7TxBroadcast FlexInt `json:"port_7-tx_broadcast"`
Port9RxPackets FlexInt `json:"port_9-rx_packets,omitempty"`
Port9RxBytes FlexInt `json:"port_9-rx_bytes,omitempty"`
Port9TxPackets FlexInt `json:"port_9-tx_packets,omitempty"`
Port9TxBytes FlexInt `json:"port_9-tx_bytes,omitempty"`
Port9TxMulticast FlexInt `json:"port_9-tx_multicast"`
Port9TxBroadcast FlexInt `json:"port_9-tx_broadcast"`
Port10RxPackets FlexInt `json:"port_10-rx_packets,omitempty"`
Port10RxBytes FlexInt `json:"port_10-rx_bytes,omitempty"`
Port10TxPackets FlexInt `json:"port_10-tx_packets,omitempty"`
Port10TxBytes FlexInt `json:"port_10-tx_bytes,omitempty"`
Port10RxMulticast FlexInt `json:"port_10-rx_multicast"`
Port10TxMulticast FlexInt `json:"port_10-tx_multicast"`
Port10TxBroadcast FlexInt `json:"port_10-tx_broadcast"`
Port11RxPackets FlexInt `json:"port_11-rx_packets,omitempty"`
Port11RxBytes FlexInt `json:"port_11-rx_bytes,omitempty"`
Port11TxPackets FlexInt `json:"port_11-tx_packets,omitempty"`
Port11TxBytes FlexInt `json:"port_11-tx_bytes,omitempty"`
Port11TxMulticast FlexInt `json:"port_11-tx_multicast"`
Port11TxBroadcast FlexInt `json:"port_11-tx_broadcast"`
Port12RxPackets FlexInt `json:"port_12-rx_packets,omitempty"`
Port12RxBytes FlexInt `json:"port_12-rx_bytes,omitempty"`
Port12TxPackets FlexInt `json:"port_12-tx_packets,omitempty"`
Port12TxBytes FlexInt `json:"port_12-tx_bytes,omitempty"`
Port12TxMulticast FlexInt `json:"port_12-tx_multicast"`
Port12TxBroadcast FlexInt `json:"port_12-tx_broadcast"`
Port13RxPackets FlexInt `json:"port_13-rx_packets,omitempty"`
Port13RxBytes FlexInt `json:"port_13-rx_bytes,omitempty"`
Port13TxPackets FlexInt `json:"port_13-tx_packets,omitempty"`
Port13TxBytes FlexInt `json:"port_13-tx_bytes,omitempty"`
Port13RxMulticast FlexInt `json:"port_13-rx_multicast"`
Port13RxBroadcast FlexInt `json:"port_13-rx_broadcast"`
Port13TxMulticast FlexInt `json:"port_13-tx_multicast"`
Port13TxBroadcast FlexInt `json:"port_13-tx_broadcast"`
Port15RxPackets FlexInt `json:"port_15-rx_packets,omitempty"`
Port15RxBytes FlexInt `json:"port_15-rx_bytes,omitempty"`
Port15TxPackets FlexInt `json:"port_15-tx_packets,omitempty"`
Port15TxBytes FlexInt `json:"port_15-tx_bytes,omitempty"`
Port15RxBroadcast FlexInt `json:"port_15-rx_broadcast"`
Port15TxMulticast FlexInt `json:"port_15-tx_multicast"`
Port15TxBroadcast FlexInt `json:"port_15-tx_broadcast"`
Port16RxPackets FlexInt `json:"port_16-rx_packets,omitempty"`
Port16RxBytes FlexInt `json:"port_16-rx_bytes,omitempty"`
Port16TxPackets FlexInt `json:"port_16-tx_packets,omitempty"`
Port16TxBytes FlexInt `json:"port_16-tx_bytes,omitempty"`
Port16TxMulticast FlexInt `json:"port_16-tx_multicast"`
Port16TxBroadcast FlexInt `json:"port_16-tx_broadcast"`
Port17RxPackets FlexInt `json:"port_17-rx_packets,omitempty"`
Port17RxBytes FlexInt `json:"port_17-rx_bytes,omitempty"`
Port17TxPackets FlexInt `json:"port_17-tx_packets,omitempty"`
Port17TxBytes FlexInt `json:"port_17-tx_bytes,omitempty"`
Port17TxMulticast FlexInt `json:"port_17-tx_multicast"`
Port17TxBroadcast FlexInt `json:"port_17-tx_broadcast"`
Port18RxPackets FlexInt `json:"port_18-rx_packets,omitempty"`
Port18RxBytes FlexInt `json:"port_18-rx_bytes,omitempty"`
Port18TxPackets FlexInt `json:"port_18-tx_packets,omitempty"`
Port18TxBytes FlexInt `json:"port_18-tx_bytes,omitempty"`
Port18RxMulticast FlexInt `json:"port_18-rx_multicast"`
Port18TxMulticast FlexInt `json:"port_18-tx_multicast"`
Port18TxBroadcast FlexInt `json:"port_18-tx_broadcast"`
Port19RxPackets FlexInt `json:"port_19-rx_packets,omitempty"`
Port19RxBytes FlexInt `json:"port_19-rx_bytes,omitempty"`
Port19TxPackets FlexInt `json:"port_19-tx_packets,omitempty"`
Port19TxBytes FlexInt `json:"port_19-tx_bytes,omitempty"`
Port19TxMulticast FlexInt `json:"port_19-tx_multicast"`
Port19TxBroadcast FlexInt `json:"port_19-tx_broadcast"`
Port21RxPackets FlexInt `json:"port_21-rx_packets,omitempty"`
Port21RxBytes FlexInt `json:"port_21-rx_bytes,omitempty"`
Port21TxPackets FlexInt `json:"port_21-tx_packets,omitempty"`
Port21TxBytes FlexInt `json:"port_21-tx_bytes,omitempty"`
Port21RxBroadcast FlexInt `json:"port_21-rx_broadcast"`
Port21TxMulticast FlexInt `json:"port_21-tx_multicast"`
Port21TxBroadcast FlexInt `json:"port_21-tx_broadcast"`
Port22RxPackets FlexInt `json:"port_22-rx_packets,omitempty"`
Port22RxBytes FlexInt `json:"port_22-rx_bytes,omitempty"`
Port22TxPackets FlexInt `json:"port_22-tx_packets,omitempty"`
Port22TxBytes FlexInt `json:"port_22-tx_bytes,omitempty"`
Port22RxMulticast FlexInt `json:"port_22-rx_multicast"`
Port22TxMulticast FlexInt `json:"port_22-tx_multicast"`
Port22TxBroadcast FlexInt `json:"port_22-tx_broadcast"`
Port23RxPackets FlexInt `json:"port_23-rx_packets,omitempty"`
Port23RxBytes FlexInt `json:"port_23-rx_bytes,omitempty"`
Port23RxDropped FlexInt `json:"port_23-rx_dropped"`
Port23TxPackets FlexInt `json:"port_23-tx_packets,omitempty"`
Port23TxBytes FlexInt `json:"port_23-tx_bytes,omitempty"`
Port23RxMulticast FlexInt `json:"port_23-rx_multicast"`
Port23RxBroadcast FlexInt `json:"port_23-rx_broadcast"`
Port23TxMulticast FlexInt `json:"port_23-tx_multicast"`
Port23TxBroadcast FlexInt `json:"port_23-tx_broadcast"`
Port24RxPackets FlexInt `json:"port_24-rx_packets,omitempty"`
Port24RxBytes FlexInt `json:"port_24-rx_bytes,omitempty"`
Port24TxPackets FlexInt `json:"port_24-tx_packets,omitempty"`
Port24TxBytes FlexInt `json:"port_24-tx_bytes,omitempty"`
Port24RxMulticast FlexInt `json:"port_24-rx_multicast"`
Port24TxMulticast FlexInt `json:"port_24-tx_multicast"`
Port24TxBroadcast FlexInt `json:"port_24-tx_broadcast"`
Port1RxMulticast FlexInt `json:"port_1-rx_multicast"`
Port3RxDropped FlexInt `json:"port_3-rx_dropped"`
Port3RxMulticast FlexInt `json:"port_3-rx_multicast"`
Port6RxDropped FlexInt `json:"port_6-rx_dropped"`
Port7RxDropped FlexInt `json:"port_7-rx_dropped"`
Port7RxMulticast FlexInt `json:"port_7-rx_multicast"`
Port9RxDropped FlexInt `json:"port_9-rx_dropped"`
Port9RxMulticast FlexInt `json:"port_9-rx_multicast"`
Port9RxBroadcast FlexInt `json:"port_9-rx_broadcast"`
Port10RxBroadcast FlexInt `json:"port_10-rx_broadcast"`
Port12RxDropped FlexInt `json:"port_12-rx_dropped"`
Port12RxMulticast FlexInt `json:"port_12-rx_multicast"`
Port13RxDropped FlexInt `json:"port_13-rx_dropped"`
Port17RxDropped FlexInt `json:"port_17-rx_dropped"`
Port17RxMulticast FlexInt `json:"port_17-rx_multicast"`
Port17RxBroadcast FlexInt `json:"port_17-rx_broadcast"`
Port19RxDropped FlexInt `json:"port_19-rx_dropped"`
Port19RxMulticast FlexInt `json:"port_19-rx_multicast"`
Port19RxBroadcast FlexInt `json:"port_19-rx_broadcast"`
Port21RxDropped FlexInt `json:"port_21-rx_dropped"`
Port21RxMulticast FlexInt `json:"port_21-rx_multicast"`
Port7RxBroadcast FlexInt `json:"port_7-rx_broadcast"`
Port18RxBroadcast FlexInt `json:"port_18-rx_broadcast"`
Port16RxMulticast FlexInt `json:"port_16-rx_multicast"`
Port15RxDropped FlexInt `json:"port_15-rx_dropped"`
Port15RxMulticast FlexInt `json:"port_15-rx_multicast"`
Port16RxBroadcast FlexInt `json:"port_16-rx_broadcast"`
Port11RxBroadcast FlexInt `json:"port_11-rx_broadcast"`
Port12RxBroadcast FlexInt `json:"port_12-rx_broadcast"`
Port6RxBroadcast FlexInt `json:"port_6-rx_broadcast"`
Port24RxBroadcast FlexInt `json:"port_24-rx_broadcast"`
Port22RxBroadcast FlexInt `json:"port_22-rx_broadcast"`
Port10TxDropped FlexInt `json:"port_10-tx_dropped"`
Port16TxDropped FlexInt `json:"port_16-tx_dropped"`
Port1RxBroadcast FlexInt `json:"port_1-rx_broadcast"`
Port4RxPackets FlexInt `json:"port_4-rx_packets,omitempty"`
Port4RxBytes FlexInt `json:"port_4-rx_bytes,omitempty"`
Port4RxDropped FlexInt `json:"port_4-rx_dropped"`
Port4TxPackets FlexInt `json:"port_4-tx_packets,omitempty"`
Port4TxBytes FlexInt `json:"port_4-tx_bytes,omitempty"`
Port4TxDropped FlexInt `json:"port_4-tx_dropped"`
Port4RxMulticast FlexInt `json:"port_4-rx_multicast"`
Port4RxBroadcast FlexInt `json:"port_4-rx_broadcast"`
Port4TxMulticast FlexInt `json:"port_4-tx_multicast"`
Port4TxBroadcast FlexInt `json:"port_4-tx_broadcast"`
*/
}
// UnmarshalJSON unmarshalls 5.10 or 5.11 formatted Switch Stat data.
func (v *USWStat) UnmarshalJSON(data []byte) error {
var n struct {
Sw `json:"sw"`
}
v.Sw = &n.Sw
err := json.Unmarshal(data, v.Sw) // controller version 5.10.
if err != nil {
return json.Unmarshal(data, &n) // controller version 5.11.
}
return nil
}

75
core/unifi/v4/usw_test.go Normal file
View File

@ -0,0 +1,75 @@
package unifi
import (
"testing"
"github.com/stretchr/testify/assert"
)
func testGetControllerJSON() (string, string) {
return `{
"sw": {
"site_id": "mySite",
"o": "sw",
"oid": "00:00:00:00:00:00",
"sw": "00:00:00:00:00:00",
"time": 1577742600000,
"datetime": "2019-12-30T09:40:00Z",
"rx_packets": 321,
"rx_bytes": 321,
"rx_errors": 123,
"rx_dropped": 123,
"rx_crypts": 123,
"rx_frags": 123,
"tx_packets": 123,
"tx_bytes": 123,
"tx_errors": 0,
"tx_dropped": 0,
"tx_retries": 0,
"rx_multicast": 123,
"rx_broadcast": 123,
"tx_multicast": 123,
"tx_broadcast": 123,
"bytes": 123,
"duration": 123}}`,
`{
"site_id": "mySite",
"o": "sw",
"oid": "00:00:00:00:00:00",
"sw": "00:00:00:00:00:00",
"time": 1577742600000,
"datetime": "2019-12-30T09:40:00Z",
"rx_packets": 321,
"rx_bytes": 321,
"rx_errors": 123,
"rx_dropped": 123,
"rx_crypts": 123,
"rx_frags": 123,
"tx_packets": 123,
"tx_bytes": 123,
"tx_errors": 0,
"tx_dropped": 0,
"tx_retries": 0,
"rx_multicast": 123,
"rx_broadcast": 123,
"tx_multicast": 123,
"tx_broadcast": 123,
"bytes": 123,
"duration": 123}`
}
func TestUSWUnmarshalJSON(t *testing.T) {
t.Parallel()
a := assert.New(t)
testcontroller511, testcontroller510 := testGetControllerJSON()
rxMulticast := 123
u := &USWStat{}
err := u.UnmarshalJSON([]byte(testcontroller510))
a.Nil(err, "must be no error unmarshaling test strings")
a.Equal(float64(rxMulticast), u.RxMulticast.Val, "data was not properly unmarshaled")
u = &USWStat{} // reset
err = u.UnmarshalJSON([]byte(testcontroller511))
a.Nil(err, "must be no error unmarshaling test strings")
a.Equal(float64(rxMulticast), u.RxMulticast.Val, "data was not properly unmarshaled")
}