leave unifi into separate repo

This commit is contained in:
Cody Lee 2022-12-02 20:44:21 -05:00
parent c811ebc1bb
commit 1369561930
No known key found for this signature in database
70 changed files with 42 additions and 9638 deletions

View File

@ -1,22 +0,0 @@
MIT LICENSE.
Copyright (c) 2018-2020 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.

View File

@ -1,65 +0,0 @@
# 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/unpoller/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/unpoller/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 "github.com/unpoller/unpoller/core/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)
}
}
```

View File

@ -1,120 +0,0 @@
package unifi
import (
"fmt"
"sort"
"time"
)
type Alarm struct {
Archived FlexBool `json:"archived"`
DestPort int `json:"dest_port"`
SrcPort int `json:"src_port"`
FlowID int64 `json:"flow_id"`
InnerAlertGID int64 `json:"inner_alert_gid"`
InnerAlertRev int64 `json:"inner_alert_rev"`
InnerAlertSeverity int64 `json:"inner_alert_severity"`
InnerAlertSignatureID int64 `json:"inner_alert_signature_id"`
Time int64 `json:"time"`
Timestamp int64 `json:"timestamp"`
Datetime time.Time `json:"datetime"`
HandledTime time.Time `json:"handled_time,omitempty"`
AppProto string `json:"app_proto,omitempty"`
Catname string `json:"catname"`
DestIP string `json:"dest_ip"`
DstMAC string `json:"dst_mac"`
DstIPASN string `json:"dstipASN,omitempty"`
DstIPCountry string `json:"dstipCountry,omitempty"`
EventType string `json:"event_type"`
HandledAdminID string `json:"handled_admin_id,omitempty"`
Host string `json:"host"`
ID string `json:"_id"`
InIface string `json:"in_iface"`
InnerAlertAction string `json:"inner_alert_action"`
InnerAlertCategory string `json:"inner_alert_category"`
InnerAlertSignature string `json:"inner_alert_signature"`
Key string `json:"key"`
Msg string `json:"msg"`
Proto string `json:"proto"`
SiteID string `json:"site_id"`
SiteName string `json:"-"`
SourceName string `json:"-"`
SrcIP string `json:"src_ip"`
SrcIPASN string `json:"srcipASN,omitempty"`
SrcIPCountry string `json:"srcipCountry,omitempty"`
SrcMAC string `json:"src_mac"`
Subsystem string `json:"subsystem"`
UniqueAlertID string `json:"unique_alertid"`
USGIP string `json:"usgip"`
USGIPASN string `json:"usgipASN"`
USGIPCountry string `json:"usgipCountry"`
TxID FlexInt `json:"tx_id,omitempty"`
DestIPGeo IPGeo `json:"dstipGeo"`
SourceIPGeo IPGeo `json:"usgipGeo"`
USGIPGeo IPGeo `json:"srcipGeo,omitempty"`
}
// GetAlarms returns Alarms for a list of Sites.
func (u *Unifi) GetAlarms(sites []*Site) ([]*Alarm, error) {
data := []*Alarm{}
for _, site := range sites {
response, err := u.GetAlarmsSite(site)
if err != nil {
return data, err
}
data = append(data, response...)
}
return data, nil
}
// GetAlarmsSite retreives the Alarms for a single Site.
func (u *Unifi) GetAlarmsSite(site *Site) ([]*Alarm, error) {
if site == nil || site.Name == "" {
return nil, ErrNoSiteProvided
}
u.DebugLog("Polling Controller for Alarms, site %s", site.SiteName)
var (
path = fmt.Sprintf(APIEventPathAlarms, site.Name)
alarms struct {
Data alarms `json:"data"`
}
)
if err := u.GetData(path, &alarms, ""); err != nil {
return alarms.Data, err
}
for i := range alarms.Data {
// Add special SourceName value.
alarms.Data[i].SourceName = u.URL
// Add the special "Site Name" to each event. This becomes a Grafana filter somewhere.
alarms.Data[i].SiteName = site.SiteName
}
sort.Sort(alarms.Data)
return alarms.Data, nil
}
// alarms satisfies the sort.Sort Interface.
type alarms []*Alarm
// Len satisfies sort.Interface.
func (a alarms) Len() int {
return len(a)
}
// Swap satisfies sort.Interface.
func (a alarms) Swap(i, j int) {
a[i], a[j] = a[j], a[i]
}
// Less satisfies sort.Interface. Sort our list by Datetime.
func (a alarms) Less(i, j int) bool {
return a[i].Datetime.Before(a[j].Datetime)
}

View File

@ -1,130 +0,0 @@
package unifi
import (
"fmt"
"sort"
"strconv"
"strings"
"time"
)
// anomaly is the type UniFi returns, but not the type this library returns.
type anomaly struct {
Anomaly string `json:"anomaly"`
MAC string `json:"mac"`
Timestamps []int64 `json:"timestamps"`
}
// Anomaly is the reformatted data type that this library returns.
type Anomaly struct {
Datetime time.Time
SourceName string
SiteName string
Anomaly string
DeviceMAC string
// DeviceName string // we do not have this....
}
// GetAnomalies returns Anomalies for a list of Sites.
func (u *Unifi) GetAnomalies(sites []*Site, timeRange ...time.Time) ([]*Anomaly, error) {
data := []*Anomaly{}
for _, site := range sites {
response, err := u.GetAnomaliesSite(site, timeRange...)
if err != nil {
return data, err
}
data = append(data, response...)
}
return data, nil
}
// GetAnomaliesSite retreives the Anomalies for a single Site.
func (u *Unifi) GetAnomaliesSite(site *Site, timeRange ...time.Time) ([]*Anomaly, error) {
if site == nil || site.Name == "" {
return nil, ErrNoSiteProvided
}
u.DebugLog("Polling Controller for Anomalies, site %s", site.SiteName)
var (
path = fmt.Sprintf(APIAnomaliesPath, site.Name)
anomalies = anomalies{}
data struct {
Data []*anomaly `json:"data"`
}
)
if params, err := makeAnomalyParams("hourly", timeRange...); err != nil {
return anomalies, err
} else if err := u.GetData(path+params, &data, ""); err != nil {
return anomalies, err
}
for _, d := range data.Data {
for _, ts := range d.Timestamps {
anomalies = append(anomalies, &Anomaly{
Datetime: time.Unix(ts/int64(time.Microsecond), 0),
SourceName: u.URL,
SiteName: site.SiteName,
Anomaly: d.Anomaly,
DeviceMAC: d.MAC,
// DeviceName: d.Anomaly,
})
}
}
sort.Sort(anomalies)
return anomalies, nil
}
// anomalies satisfies the sort.Sort interface.
type anomalies []*Anomaly
// Len satisfies sort.Interface.
func (a anomalies) Len() int {
return len(a)
}
// Swap satisfies sort.Interface.
func (a anomalies) Swap(i, j int) {
a[i], a[j] = a[j], a[i]
}
// Less satisfies sort.Interface. Sort our list by Datetime.
func (a anomalies) Less(i, j int) bool {
return a[i].Datetime.Before(a[j].Datetime)
}
func makeAnomalyParams(scale string, timeRange ...time.Time) (string, error) {
out := []string{}
if scale != "" {
out = append(out, "scale="+scale)
}
switch len(timeRange) {
case 0:
end := time.Now().Unix() * int64(time.Microsecond)
out = append(out, "end="+strconv.FormatInt(end, 10))
case 1:
start := timeRange[0].Unix() * int64(time.Microsecond)
end := time.Now().Unix() * int64(time.Microsecond)
out = append(out, "end="+strconv.FormatInt(end, 10), "start="+strconv.FormatInt(start, 10))
case 2: // nolint: gomnd
start := timeRange[0].Unix() * int64(time.Microsecond)
end := timeRange[1].Unix() * int64(time.Microsecond)
out = append(out, "end="+strconv.FormatInt(end, 10), "start="+strconv.FormatInt(start, 10))
default:
return "", ErrInvalidTimeRange
}
if len(out) == 0 {
return "", nil
}
return "?" + strings.Join(out, "&"), nil
}

View File

@ -1,154 +0,0 @@
package unifi
import (
"fmt"
"strings"
)
// GetClients returns a response full of clients' data from the UniFi Controller.
func (u *Unifi) GetClients(sites []*Site) ([]*Client, 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 ", site.SiteName)
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.SiteName
// Fix name and hostname fields. Sometimes one or the other is blank.
response.Data[i].Hostname = strings.TrimSpace(pick(d.Hostname, d.Name, d.Mac))
response.Data[i].Name = strings.TrimSpace(pick(d.Name, d.Hostname))
}
data = append(data, response.Data...)
}
return data, nil
}
// GetClientsDPI garners dpi data for clients.
func (u *Unifi) GetClientsDPI(sites []*Site) ([]*DPITable, error) {
var data []*DPITable
for _, site := range sites {
u.DebugLog("Polling Controller, retreiving Client DPI data, site %s", site.SiteName)
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
}
// 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 FlexInt `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"`
}

View File

@ -1,213 +0,0 @@
package unifi
import (
"encoding/json"
"fmt"
"strings"
)
// GetDevices returns a response full of devices' data from the UniFi Controller.
func (u *Unifi) GetDevices(sites []*Site) (*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)
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...)
devices.UXGs = append(devices.UXGs, loopDevices.UXGs...)
}
return devices, nil
}
// GetUSWs returns all switches, an error, or nil if there are no switches.
func (u *Unifi) GetUSWs(site *Site) ([]*USW, error) {
var response struct {
Data []json.RawMessage `json:"data"`
}
err := u.GetData(fmt.Sprintf(APIDevicePath, site.Name), &response)
if err != nil {
return nil, err
}
return u.parseDevices(response.Data, site).USWs, nil
}
// GetUAPs returns all access points, an error, or nil if there are no APs.
func (u *Unifi) GetUAPs(site *Site) ([]*UAP, error) {
var response struct {
Data []json.RawMessage `json:"data"`
}
err := u.GetData(fmt.Sprintf(APIDevicePath, site.Name), &response)
if err != nil {
return nil, err
}
return u.parseDevices(response.Data, site).UAPs, nil
}
// GetUDMs returns all dream machines, an error, or nil if there are no UDMs.
func (u *Unifi) GetUDMs(site *Site) ([]*UDM, error) {
var response struct {
Data []json.RawMessage `json:"data"`
}
err := u.GetData(fmt.Sprintf(APIDevicePath, site.Name), &response)
if err != nil {
return nil, err
}
return u.parseDevices(response.Data, site).UDMs, nil
}
// GetUXGs returns all 10Gb gateways, an error, or nil if there are no UXGs.
func (u *Unifi) GetUXGs(site *Site) ([]*UXG, error) {
var response struct {
Data []json.RawMessage `json:"data"`
}
err := u.GetData(fmt.Sprintf(APIDevicePath, site.Name), &response)
if err != nil {
return nil, err
}
return u.parseDevices(response.Data, site).UXGs, nil
}
// GetUSGs returns all 1Gb gateways, an error, or nil if there are no USGs.
func (u *Unifi) GetUSGs(site *Site) ([]*USG, error) {
var response struct {
Data []json.RawMessage `json:"data"`
}
err := u.GetData(fmt.Sprintf(APIDevicePath, site.Name), &response)
if err != nil {
return nil, err
}
return u.parseDevices(response.Data, site).USGs, nil
}
// parseDevices parses the raw JSON from the Unifi Controller into device structures.
func (u *Unifi) parseDevices(data []json.RawMessage, site *Site) *Devices {
devices := new(Devices)
for _, r := range data {
// Loop each item in the raw JSON message, detect its type and unmarshal it.
o := make(map[string]interface{})
if u.unmarshalDevice("map", r, &o) != nil {
u.ErrorLog("unknown asset type - cannot find asset type in payload - skipping")
continue
}
assetType, _ := o["type"].(string)
u.DebugLog("Unmarshalling Device Type: %v, site %s ", assetType, site.SiteName)
// Choose which type to unmarshal into based on the "type" json key.
switch assetType { // Unmarshal again into the correct type..
case "uap":
u.unmarshallUAP(site, r, devices)
case "ugw", "usg": // in case they ever fix the name in the api.
u.unmarshallUSG(site, r, devices)
case "usw":
u.unmarshallUSW(site, r, devices)
case "udm":
u.unmarshallUDM(site, r, devices)
case "uxg":
u.unmarshallUXG(site, r, devices)
default:
u.ErrorLog("unknown asset type - %v - skipping", assetType)
}
}
return devices
}
func (u *Unifi) unmarshallUAP(site *Site, payload json.RawMessage, devices *Devices) {
dev := &UAP{SiteName: site.SiteName, SourceName: u.URL}
if u.unmarshalDevice("uap", payload, dev) == nil {
dev.Name = strings.TrimSpace(pick(dev.Name, dev.Mac))
dev.site = site
devices.UAPs = append(devices.UAPs, dev)
}
}
func (u *Unifi) unmarshallUSG(site *Site, payload json.RawMessage, devices *Devices) {
dev := &USG{SiteName: site.SiteName, SourceName: u.URL}
if u.unmarshalDevice("ugw", payload, dev) == nil {
dev.Name = strings.TrimSpace(pick(dev.Name, dev.Mac))
dev.site = site
devices.USGs = append(devices.USGs, dev)
}
}
func (u *Unifi) unmarshallUSW(site *Site, payload json.RawMessage, devices *Devices) {
dev := &USW{SiteName: site.SiteName, SourceName: u.URL}
if u.unmarshalDevice("usw", payload, dev) == nil {
dev.Name = strings.TrimSpace(pick(dev.Name, dev.Mac))
dev.site = site
devices.USWs = append(devices.USWs, dev)
}
}
func (u *Unifi) unmarshallUXG(site *Site, payload json.RawMessage, devices *Devices) {
dev := &UXG{SiteName: site.SiteName, SourceName: u.URL}
if u.unmarshalDevice("uxg", payload, dev) == nil {
dev.Name = strings.TrimSpace(pick(dev.Name, dev.Mac))
dev.site = site
devices.UXGs = append(devices.UXGs, dev)
}
}
func (u *Unifi) unmarshallUDM(site *Site, payload json.RawMessage, devices *Devices) {
dev := &UDM{SiteName: site.SiteName, SourceName: u.URL}
if u.unmarshalDevice("udm", payload, dev) == nil {
dev.Name = strings.TrimSpace(pick(dev.Name, dev.Mac))
dev.site = site
devices.UDMs = append(devices.UDMs, dev)
}
}
// 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/unifi-poller/unifi/issues/new -==")
}
if err != nil {
return fmt.Errorf("json unmarshal: %w", err)
}
return nil
}
// 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 ""
}

View File

@ -1,299 +0,0 @@
package unifi
import (
"encoding/json"
"fmt"
)
// Known commands that can be sent to device manager. All of these are implemented.
//nolint:lll // https://ubntwiki.com/products/software/unifi-controller/api#callable
const (
DevMgrPowerCycle = "power-cycle" // mac = switch mac (required), port_idx = PoE port to cycle (required)
DevMgrAdopt = "adopt" // mac = device mac (required)
DevMgrRestart = "restart" // mac = device mac (required)
DevMgrForceProvision = "force-provision" // mac = device mac (required)
DevMgrSpeedTest = "speedtest" // Start a speed test
DevMgrSpeedTestStatus = "speedtest-status" // Get current state of the speed test
DevMgrSetLocate = "set-locate" // mac = device mac (required): blink unit to locate
DevMgrUnsetLocate = "unset-locate" // mac = device mac (required): led to normal state
DevMgrUpgrade = "upgrade" // mac = device mac (required): upgrade firmware
DevMgrUpgradeExternal = "upgrade-external" // mac = device mac (required), url = firmware URL (required)
DevMgrMigrate = "migrate" // mac = device mac (required), inform_url = New Inform URL for device (required)
DevMgrCancelMigrate = "cancel-migrate" // mac = device mac (required)
DevMgrSpectrumScan = "spectrum-scan" // mac = AP mac (required): trigger RF scan
)
// devMgrCmd is the type marshalled and sent to APIDevMgrPath.
type devMgrCmd struct {
Cmd string `json:"cmd"` // Required.
Mac string `json:"mac"` // Device MAC (required for most, but not all).
URL string `json:"url,omitempty"` // External Upgrade only.
Inform string `json:"inform_url,omitempty"` // Migration only.
Port int `json:"port_idx,omitempty"` // Power Cycle only.
}
// devMgrCommandReply is for commands with a return value.
func (s *Site) devMgrCommandReply(cmd *devMgrCmd) ([]byte, error) {
data, err := json.Marshal(cmd)
if err != nil {
return nil, fmt.Errorf("json marshal: %w", err)
}
b, err := s.controller.GetJSON(fmt.Sprintf(APIDevMgrPath, s.Name), string(data))
if err != nil {
return nil, fmt.Errorf("controller: %w", err)
}
return b, nil
}
// devMgrCommandSimple is for commands with no return value.
func (s *Site) devMgrCommandSimple(cmd *devMgrCmd) error {
_, err := s.devMgrCommandReply(cmd)
return err
}
// PowerCycle shuts off the PoE and turns it back on for a specific port.
// Get a USW from the device list to call this.
func (u *USW) PowerCycle(portIndex int) error {
return u.site.devMgrCommandSimple(&devMgrCmd{
Cmd: DevMgrPowerCycle,
Mac: u.Mac,
Port: portIndex,
})
}
// ScanRF begins a spectrum scan on an access point.
func (u *UAP) ScanRF() error {
return u.site.devMgrCommandSimple(&devMgrCmd{Cmd: DevMgrSpectrumScan, Mac: u.Mac})
}
// Restart a device by MAC address on your site.
func (s *Site) Restart(mac string) error {
return s.devMgrCommandSimple(&devMgrCmd{Cmd: DevMgrRestart, Mac: mac})
}
// Restart an access point.
func (u *UAP) Restart() error {
return u.site.Restart(u.Mac)
}
// Restart a switch.
func (u *USW) Restart() error {
return u.site.Restart(u.Mac)
}
// Restart a security gateway.
func (u *USG) Restart() error {
return u.site.Restart(u.Mac)
}
// Restart a dream machine.
func (u *UDM) Restart() error {
return u.site.Restart(u.Mac)
}
// Restart a 10Gb security gateway.
func (u *UXG) Restart() error {
return u.site.Restart(u.Mac)
}
// Locate a device by MAC address on your site. This makes it blink.
func (s *Site) Locate(mac string) error {
return s.devMgrCommandSimple(&devMgrCmd{Cmd: DevMgrSetLocate, Mac: mac})
}
// Locate an access point.
func (u *UAP) Locate() error {
return u.site.Locate(u.Mac)
}
// Locate a switch.
func (u *USW) Locate() error {
return u.site.Locate(u.Mac)
}
// Locate a security gateway.
func (u *USG) Locate() error {
return u.site.Locate(u.Mac)
}
// Locate a dream machine.
func (u *UDM) Locate() error {
return u.site.Locate(u.Mac)
}
// Locate a 10Gb security gateway.
func (u *UXG) Locate() error {
return u.site.Locate(u.Mac)
}
// Unlocate a device by MAC address on your site. This makes it stop blinking.
func (s *Site) Unlocate(mac string) error {
return s.devMgrCommandSimple(&devMgrCmd{Cmd: DevMgrUnsetLocate, Mac: mac})
}
// Unlocate an access point (stop blinking).
func (u *UAP) Unlocate() error {
return u.site.Unlocate(u.Mac)
}
// Unlocate a switch (stop blinking).
func (u *USW) Unlocate() error {
return u.site.Unlocate(u.Mac)
}
// Unlocate a security gateway (stop blinking).
func (u *USG) Unlocate() error {
return u.site.Unlocate(u.Mac)
}
// Unlocate a dream machine (stop blinking).
func (u *UDM) Unlocate() error {
return u.site.Unlocate(u.Mac)
}
// Unlocate a 10Gb security gateway (stop blinking).
func (u *UXG) Unlocate() error {
return u.site.Unlocate(u.Mac)
}
// Provision force provisions a device by MAC address on your site.
func (s *Site) Provision(mac string) error {
return s.devMgrCommandSimple(&devMgrCmd{Cmd: DevMgrForceProvision, Mac: mac})
}
// Provision an access point forcefully.
func (u *UAP) Provision() error {
return u.site.Provision(u.Mac)
}
// Provision a switch forcefully.
func (u *USW) Provision() error {
return u.site.Provision(u.Mac)
}
// Provision a security gateway forcefully.
func (u *USG) Provision() error {
return u.site.Provision(u.Mac)
}
// Provision a dream machine forcefully.
func (u *UDM) Provision() error {
return u.site.Provision(u.Mac)
}
// Provision a 10Gb security gateway forcefully.
func (u *UXG) Provision() error {
return u.site.Provision(u.Mac)
}
// Upgrade starts a firmware upgrade on a device by MAC address on your site.
// URL is optional. If URL is not "" an external upgrade is performed.
func (s *Site) Upgrade(mac string, url string) error {
if url == "" {
return s.devMgrCommandSimple(&devMgrCmd{Cmd: DevMgrUpgrade, Mac: mac})
}
return s.devMgrCommandSimple(&devMgrCmd{Cmd: DevMgrUpgradeExternal, Mac: mac, URL: url})
}
// Upgrade firmware on an access point.
// URL is optional. If URL is not "" an external upgrade is performed.
func (u *UAP) Upgrade(url string) error {
return u.site.Upgrade(u.Mac, url)
}
// Upgrade firmware on a switch.
// URL is optional. If URL is not "" an external upgrade is performed.
func (u *USW) Upgrade(url string) error {
return u.site.Upgrade(u.Mac, url)
}
// Upgrade firmware on a security gateway.
// URL is optional. If URL is not "" an external upgrade is performed.
func (u *USG) Upgrade(url string) error {
return u.site.Upgrade(u.Mac, url)
}
// Upgrade firmware on a dream machine.
// URL is optional. If URL is not "" an external upgrade is performed.
func (u *UDM) Upgrade(url string) error {
return u.site.Upgrade(u.Mac, url)
}
// Upgrade formware on a 10Gb security gateway.
// URL is optional. If URL is not "" an external upgrade is performed.
func (u *UXG) Upgrade(url string) error {
return u.site.Upgrade(u.Mac, url)
}
// Migrate sends a device to another controller's URL.
// Probably does not work on devices with built-in controllers like UDM & UXG.
func (s *Site) Migrate(mac string, url string) error {
return s.devMgrCommandSimple(&devMgrCmd{Cmd: DevMgrMigrate, Mac: mac, Inform: url})
}
// Migrate sends an access point to another controller's URL.
func (u *UAP) Migrate(url string) error {
return u.site.Migrate(u.Mac, url)
}
// Migrate sends a switch to another controller's URL.
func (u *USW) Migrate(url string) error {
return u.site.Migrate(u.Mac, url)
}
// Migrate sends a security gateway to another controller's URL.
func (u *USG) Migrate(url string) error {
return u.site.Migrate(u.Mac, url)
}
// Migrate sends a 10Gb gateway to another controller's URL.
func (u *UXG) Migrate(url string) error {
return u.site.Migrate(u.Mac, url)
}
// CancelMigrate stops a migration in progress.
// Probably does not work on devices with built-in controllers like UDM & UXG.
func (s *Site) CancelMigrate(mac string) error {
return s.devMgrCommandSimple(&devMgrCmd{Cmd: DevMgrCancelMigrate, Mac: mac})
}
// CancelMigrate stops an access point migration in progress.
func (u *UAP) CancelMigrate() error {
return u.site.CancelMigrate(u.Mac)
}
// CancelMigrate stops a switch migration in progress.
func (u *USW) CancelMigrate() error {
return u.site.CancelMigrate(u.Mac)
}
// CancelMigrate stops a security gateway migration in progress.
func (u *USG) CancelMigrate() error {
return u.site.CancelMigrate(u.Mac)
}
// CancelMigrate stops 10Gb gateway a migration in progress.
func (u *UXG) CancelMigrate() error {
return u.site.CancelMigrate(u.Mac)
}
// Adopt a device by MAC address to your site.
func (s *Site) Adopt(mac string) error {
return s.devMgrCommandSimple(&devMgrCmd{Cmd: DevMgrAdopt, Mac: mac})
}
// SpeedTest begins a speed test on a site.
func (s *Site) SpeedTest() error {
return s.devMgrCommandSimple(&devMgrCmd{Cmd: DevMgrSpeedTest})
}
// SpeedTestStatus returns the raw response for the status of a speed test.
// XXX: marshal the response into a data structure. This method will change!
func (s *Site) SpeedTestStatus() ([]byte, error) {
body, err := s.devMgrCommandReply(&devMgrCmd{Cmd: DevMgrSpeedTestStatus})
// marshal into struct here.
return body, err
}

File diff suppressed because it is too large Load Diff

View File

@ -1,204 +0,0 @@
package unifi
import (
"encoding/json"
"fmt"
"sort"
"time"
)
var (
ErrNoSiteProvided = fmt.Errorf("site must not be nil or empty")
ErrInvalidTimeRange = fmt.Errorf("only 0, 1 or 2 times may be provided to timeRange")
)
const (
eventLimit = 50000
)
// GetEvents returns a response full of UniFi Events for the last 1 hour from multiple sites.
func (u *Unifi) GetEvents(sites []*Site, hours time.Duration) ([]*Event, error) {
data := make([]*Event, 0)
for _, site := range sites {
response, err := u.GetSiteEvents(site, hours)
if err != nil {
return data, err
}
data = append(data, response...)
}
return data, nil
}
// GetSiteEvents retrieves the last 1 hour's worth of events from a single site.
func (u *Unifi) GetSiteEvents(site *Site, hours time.Duration) ([]*Event, error) {
if site == nil || site.Name == "" {
return nil, ErrNoSiteProvided
}
if hours < time.Hour {
hours = time.Hour
}
u.DebugLog("Polling Controller, retreiving UniFi Events, site %s", site.SiteName)
var (
path = fmt.Sprintf(APIEventPath, site.Name)
params = fmt.Sprintf(`{"_limit":%d,"within":%d,"_sort":"-time"}}`,
eventLimit, int(hours.Round(time.Hour).Hours()))
event struct {
Data events `json:"data"`
}
)
if err := u.GetData(path, &event, params); err != nil {
return event.Data, err
}
for i := range event.Data {
// Add special SourceName value.
event.Data[i].SourceName = u.URL
// Add the special "Site Name" to each event. This becomes a Grafana filter somewhere.
event.Data[i].SiteName = site.SiteName
}
sort.Sort(event.Data)
return event.Data, nil
}
// Event describes a UniFi Event.
// API Path: /api/s/default/stat/event.
type Event struct {
IsAdmin FlexBool `json:"is_admin"`
DestPort int `json:"dest_port"`
SrcPort int `json:"src_port"`
Bytes FlexInt `json:"bytes"`
Duration FlexInt `json:"duration"`
FlowID FlexInt `json:"flow_id"`
InnerAlertGID FlexInt `json:"inner_alert_gid"`
InnerAlertRev FlexInt `json:"inner_alert_rev"`
InnerAlertSeverity FlexInt `json:"inner_alert_severity"`
InnerAlertSignatureID FlexInt `json:"inner_alert_signature_id"`
Channel FlexInt `json:"channel"`
ChannelFrom FlexInt `json:"channel_from"`
ChannelTo FlexInt `json:"channel_to"`
Time int64 `json:"time"`
Timestamp int64 `json:"timestamp"`
Datetime time.Time `json:"datetime"`
Admin string `json:"admin"`
Ap string `json:"ap"`
ApFrom string `json:"ap_from"`
ApName string `json:"ap_name"`
ApTo string `json:"ap_to"`
AppProto string `json:"app_proto"`
Catname string `json:"catname"`
DestIP string `json:"dest_ip"`
DstMAC string `json:"dst_mac"`
EventType string `json:"event_type"`
Guest string `json:"guest"`
Gw string `json:"gw"`
GwName string `json:"gw_name"`
Host string `json:"host"`
Hostname string `json:"hostname"`
ID string `json:"_id"`
IP string `json:"ip"`
InIface string `json:"in_iface"`
InnerAlertAction string `json:"inner_alert_action"`
InnerAlertCategory string `json:"inner_alert_category"`
InnerAlertSignature string `json:"inner_alert_signature"`
Key string `json:"key"`
Msg string `json:"msg"`
Network string `json:"network"`
Proto string `json:"proto"`
Radio string `json:"radio"`
RadioFrom string `json:"radio_from"`
RadioTo string `json:"radio_to"`
SiteID string `json:"site_id"`
SiteName string `json:"-"`
SourceName string `json:"-"`
SrcIP string `json:"src_ip"`
SrcMAC string `json:"src_mac"`
SrcIPASN string `json:"srcipASN"`
SrcIPCountry string `json:"srcipCountry"`
SSID string `json:"ssid"`
Subsystem string `json:"subsystem"`
Sw string `json:"sw"`
SwName string `json:"sw_name"`
UniqueAlertID string `json:"unique_alertid"`
User string `json:"user"`
USGIP string `json:"usgip"`
USGIPASN string `json:"usgipASN"`
USGIPCountry string `json:"usgipCountry"`
DestIPGeo IPGeo `json:"dstipGeo"`
SourceIPGeo IPGeo `json:"srcipGeo"`
USGIPGeo IPGeo `json:"usgipGeo"`
}
// IPGeo is part of the UniFi Event data. Each event may have up to three of these.
// One for source, one for dest and one for the USG location.
type IPGeo struct {
Asn int64 `json:"asn"`
Latitude float64 `json:"latitude"`
Longitude float64 `json:"longitude"`
City string `json:"city"`
ContinentCode string `json:"continent_code"`
CountryCode string `json:"country_code"`
CountryName string `json:"country_name"`
Organization string `json:"organization"`
}
// Events satisfied the sort.Interface.
type events []*Event
// Len satisfies sort.Interface.
func (e events) Len() int {
return len(e)
}
// Swap satisfies sort.Interface.
func (e events) Swap(i, j int) {
e[i], e[j] = e[j], e[i]
}
// Less satisfies sort.Interface. Sort our list by date/time.
func (e events) Less(i, j int) bool {
return e[i].Datetime.Before(e[j].Datetime)
}
// UnmarshalJSON is required because sometimes the unifi api returns
// an empty array instead of a struct filled with data.
func (v *IPGeo) UnmarshalJSON(data []byte) error {
if string(data) == "[]" {
return nil // it's empty
}
g := struct {
Asn int64 `json:"asn"`
Latitude float64 `json:"latitude"`
Longitude float64 `json:"longitude"`
City string `json:"city"`
ContinentCode string `json:"continent_code"`
CountryCode string `json:"country_code"`
CountryName string `json:"country_name"`
Organization string `json:"organization"`
}{}
err := json.Unmarshal(data, &g)
v.Asn = g.Asn
v.Latitude = g.Latitude
v.Longitude = g.Longitude
v.City = g.City
v.ContinentCode = g.ContinentCode
v.CountryCode = g.CountryCode
v.CountryName = g.CountryName
v.Organization = g.Organization
if err != nil {
return fmt.Errorf("json unmarshal: %w", err)
}
return nil
}

View File

@ -1,20 +0,0 @@
package unifi_test
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/unpoller/unpoller/core/unifi"
)
func TestIPGeoUnmarshalJSON(t *testing.T) {
t.Parallel()
a := assert.New(t)
i := &unifi.IPGeo{}
a.Nil(i.UnmarshalJSON([]byte(`[]`)))
a.EqualValues(0, i.Asn)
a.Nil(i.UnmarshalJSON([]byte(`{"asn": 123}`)))
a.EqualValues(123, i.Asn)
}

View File

@ -1,26 +0,0 @@
#!/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

@ -1,86 +0,0 @@
{
"_id": "5ee9e572453d4e00f3c04a7c",
"user": "d8:4c:90:9f:82:5a",
"ssid": "Extra Fast",
"ap": "b4:fb:e4:d2:74:39",
"radio": "na",
"channel": "36",
"key": "EVT_WU_Connected",
"subsystem": "wlan",
"site_id": "574e86994566ffb914a2683c",
"time": 1592386923851,
"datetime": "2020-06-17T09:42:03Z",
"msg": "User[d8:4c:90:9f:82:5a] has connected to AP[b4:fb:e4:d2:74:39] with SSID \"Extra Fast\" on \"channel 36(na)\""
},
{
"_id": "5ee9e56b453d4e00f3c04a7a",
"user": "d8:4c:90:9f:82:5a",
"ssid": "Extra Fast",
"hostname": "dns-ipp",
"ap": "74:83:c2:d4:11:3d",
"duration": 1084,
"bytes": 846171,
"key": "EVT_WU_Disconnected",
"subsystem": "wlan",
"site_id": "574e86994566ffb914a2683c",
"time": 1592386923000,
"datetime": "2020-06-17T09:42:03Z",
"msg": "User[d8:4c:90:9f:82:5a] disconnected from \"Extra Fast\" (18m 4s connected, 826.34K bytes, last AP[74:83:c2:d4:11:3d])"
},
{
"_id": "5ee9f7ca453d4e00f3c04b57",
"timestamp": 1592391625,
"flow_id": 1510453960799559,
"in_iface": "eth0",
"event_type": "alert",
"src_ip": "192.168.1.199",
"src_mac": "00:50:b6:96:76:6e",
"src_port": 50447,
"dest_ip": "54.36.xxx.xxx",
"dst_mac": "74:83:c2:1a:35:39",
"dest_port": 80,
"proto": "TCP",
"tx_id": 0,
"app_proto": "http",
"host": "usg-sensor",
"usgip": "67.181.75.120",
"unique_alertid": "1603112333-2020-06-17T04:00:25.225809-0700",
"srcipGeo": [],
"dstipGeo": {
"continent_code": "EU",
"country_code": "FR",
"country_name": "France",
"latitude": 48.8582,
"longitude": 2.3387,
"asn": 16276,
"organization": "OVH SAS"
},
"dstipCountry": "FR",
"dstipASN": "16276 OVH SAS",
"usgipGeo": {
"continent_code": "NA",
"country_code": "US",
"country_name": "United States",
"city": "Lodi",
"latitude": 38.1228,
"longitude": -121.2543,
"asn": 7922,
"organization": "COMCAST-7922"
},
"usgipCountry": "US",
"usgipASN": "7922 COMCAST-7922",
"catname": "emerging-malware",
"inner_alert_action": "allowed",
"inner_alert_gid": 1,
"inner_alert_signature_id": 2003337,
"inner_alert_rev": 21,
"inner_alert_signature": "ET MALWARE Suspicious User Agent (Autoupdate)",
"inner_alert_category": "A Network Trojan was Detected",
"inner_alert_severity": 1,
"key": "EVT_IPS_IpsAlert",
"subsystem": "www",
"site_id": "574e86994566ffb914a2683c",
"time": 1592391625000,
"datetime": "2020-06-17T11:00:25Z",
"msg": "IPS Alert 1: A Network Trojan was Detected. Signature ET MALWARE Suspicious User Agent (Autoupdate). From: 192.168.1.199:50447, to: 54.36.xxx.xxx:80, protocol: TCP"
},

View File

@ -1,66 +0,0 @@
{
"_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

@ -1,738 +0,0 @@
{
"_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

@ -1,371 +0,0 @@
{
"_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

View File

@ -1,155 +0,0 @@
package unifi
import (
"encoding/json"
"fmt"
"sort"
"time"
)
// IDS holds an Intrusion Prevention System Event.
type IDS struct {
Archived FlexBool `json:"archived"`
DestPort int `json:"dest_port,omitempty"`
SrcPort int `json:"src_port,omitempty"`
FlowID int64 `json:"flow_id"`
InnerAlertRev int64 `json:"inner_alert_rev"`
InnerAlertSeverity int64 `json:"inner_alert_severity"`
InnerAlertGID int64 `json:"inner_alert_gid"`
InnerAlertSignatureID int64 `json:"inner_alert_signature_id"`
Time int64 `json:"time"`
Timestamp int64 `json:"timestamp"`
Datetime time.Time `json:"datetime"`
AppProto string `json:"app_proto,omitempty"`
Catname string `json:"catname"`
DestIP string `json:"dest_ip"`
DstMAC string `json:"dst_mac"`
DstIPASN string `json:"dstipASN"`
DstIPCountry string `json:"dstipCountry"`
EventType string `json:"event_type"`
Host string `json:"host"`
ID string `json:"_id"`
InIface string `json:"in_iface"`
InnerAlertAction string `json:"inner_alert_action"`
InnerAlertCategory string `json:"inner_alert_category"`
InnerAlertSignature string `json:"inner_alert_signature"`
Key string `json:"key"`
Msg string `json:"msg"`
Proto string `json:"proto"`
SiteID string `json:"site_id"`
SiteName string `json:"-"`
SourceName string `json:"-"`
SrcIP string `json:"src_ip"`
SrcIPASN string `json:"srcipASN"`
SrcIPCountry string `json:"srcipCountry"`
SrcMAC string `json:"src_mac"`
Subsystem string `json:"subsystem"`
UniqueAlertID string `json:"unique_alertid"`
USGIP string `json:"usgip"`
USGIPASN string `json:"usgipASN"`
USGIPCountry string `json:"usgipCountry"`
DestIPGeo IPGeo `json:"dstipGeo"`
SourceIPGeo IPGeo `json:"srcipGeo"`
USGIPGeo IPGeo `json:"usgipGeo"`
}
// GetIDS returns Intrusion Detection Systems events for a list of Sites.
// timeRange may have a length of 0, 1 or 2. The first time is Start, the second is End.
// Events between start and end are returned. End defaults to time.Now().
func (u *Unifi) GetIDS(sites []*Site, timeRange ...time.Time) ([]*IDS, error) {
data := []*IDS{}
for _, site := range sites {
response, err := u.GetIDSSite(site, timeRange...)
if err != nil {
return data, err
}
data = append(data, response...)
}
return data, nil
}
// GetIDSSite retreives the Intrusion Detection System Data for a single Site.
// timeRange may have a length of 0, 1 or 2. The first time is Start, the second is End.
// Events between start and end are returned. End defaults to time.Now().
func (u *Unifi) GetIDSSite(site *Site, timeRange ...time.Time) ([]*IDS, error) {
if site == nil || site.Name == "" {
return nil, ErrNoSiteProvided
}
u.DebugLog("Polling Controller for IDS Events, site %s", site.SiteName)
var (
path = fmt.Sprintf(APIEventPathIDS, site.Name)
ids struct {
Data idsList `json:"data"`
}
)
if params, err := makeEventParams(timeRange...); err != nil {
return ids.Data, err
} else if err = u.GetData(path, &ids, params); err != nil {
return ids.Data, err
}
for i := range ids.Data {
// Add special SourceName value.
ids.Data[i].SourceName = u.URL
// Add the special "Site Name" to each event. This becomes a Grafana filter somewhere.
ids.Data[i].SiteName = site.SiteName
}
sort.Sort(ids.Data)
return ids.Data, nil
}
func makeEventParams(timeRange ...time.Time) (string, error) {
type eventReq struct {
Start int64 `json:"start,omitempty"`
End int64 `json:"end,omitempty"`
Limit int `json:"_limit,omitempty"`
Sort string `json:"_sort"`
}
rp := eventReq{Limit: eventLimit, Sort: "-time"}
switch len(timeRange) {
case 0:
rp.End = time.Now().Unix() * int64(time.Microsecond)
case 1:
rp.Start = timeRange[0].Unix() * int64(time.Microsecond)
rp.End = time.Now().Unix() * int64(time.Microsecond)
case 2: // nolint: gomnd
rp.Start = timeRange[0].Unix() * int64(time.Microsecond)
rp.End = timeRange[1].Unix() * int64(time.Microsecond)
default:
return "", ErrInvalidTimeRange
}
params, err := json.Marshal(&rp)
if err != nil {
return "", fmt.Errorf("json marshal: %w", err)
}
return string(params), nil
}
type idsList []*IDS
// Len satisfies sort.Interface.
func (e idsList) Len() int {
return len(e)
}
// Swap satisfies sort.Interface.
func (e idsList) Swap(i, j int) {
e[i], e[j] = e[j], e[i]
}
// Less satisfies sort.Interface. Sort our list by Datetime.
func (e idsList) Less(i, j int) bool {
return e[i].Datetime.Before(e[j].Datetime)
}

View File

@ -1,62 +0,0 @@
package unifi
import (
"encoding/json"
"fmt"
)
// GetNetworks returns a response full of network data from the UniFi Controller.
func (u *Unifi) GetNetworks(sites []*Site) ([]Network, error) {
networks := make([]Network, 0)
for _, site := range sites {
var response struct {
Data []json.RawMessage `json:"data"`
}
networkPath := fmt.Sprintf(APINetworkPath, site.Name)
if err := u.GetData(networkPath, &response); err != nil {
return nil, err
}
for _, data := range response.Data {
network, err := u.parseNetwork(data, site.SiteName)
if err != nil {
return networks, err
}
networks = append(networks, *network)
}
}
return networks, nil
}
// parseNetwork parses the raw JSON from the Unifi Controller into network structures.
func (u *Unifi) parseNetwork(data json.RawMessage, siteName string) (*Network, error) {
network := new(Network)
return network, u.unmarshalDevice(siteName, data, network)
}
// Network is metadata about a network managed by a UniFi controller.
type Network struct {
DhcpdDNSEnabled FlexBool `json:"dhcpd_dns_enabled"`
DhcpdEnabled FlexBool `json:"dhcpd_enabled"`
DhcpdGatewayEnabled FlexBool `json:"dhcpd_gateway_enabled"`
DhcpdIP1 string `json:"dhcpd_ip_1"`
DhcpdLeasetime FlexInt `json:"dhcpd_leasetime"`
DhcpRelayEnabled FlexBool `json:"dhcp_relay_enabled"`
DhcpdTimeOffsetEnabled FlexBool `json:"dhcpd_time_offset_enabled"`
DhcpGuardEnabled FlexBool `json:"dhcpguard_enabled"`
DomainName string `json:"domain_name"`
Enabled FlexBool `json:"enabled"`
ID string `json:"_id"`
IPSubnet string `json:"ip_subnet"`
IsNat FlexBool `json:"is_nat"`
Name string `json:"name"`
Networkgroup string `json:"networkgroup"`
Purpose string `json:"purpose"`
SiteID string `json:"site_id"`
Vlan FlexInt `json:"vlan"`
VlanEnabled FlexBool `json:"vlan_enabled"`
}

View File

@ -1,127 +0,0 @@
package unifi
import (
"fmt"
"strings"
)
var ErrDPIDataBug = fmt.Errorf("dpi data table contains more than 1 item; please open a bug report")
// GetSites returns a list of configured sites on the UniFi controller.
func (u *Unifi) GetSites() ([]*Site, 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 the unifi struct to the site.
response.Data[i].controller = u
// 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 = strings.TrimSpace(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 []*Site) ([]*DPITable, error) {
data := []*DPITable{}
for _, site := range sites {
u.DebugLog("Polling Controller, retreiving Site DPI data, site %s", site.SiteName)
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, ErrDPIDataBug
} else if l == 0 {
u.DebugLog("Site DPI data missing! Is DPI enabled in UniFi controller? Site %s", site.SiteName)
continue
}
response.Data[0].SourceName = site.SourceName
response.Data[0].SiteName = site.SiteName
data = append(data, response.Data[0])
}
return data, nil
}
// Site represents a site's data.
type Site struct {
controller *Unifi
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"`
}

View File

@ -1,195 +0,0 @@
package unifi
import (
"encoding/json"
"fmt"
"net/http"
"strconv"
"strings"
"time"
)
var ErrCannotUnmarshalFlexInt = fmt.Errorf("cannot unmarshal to FlexInt")
// This is a list of unifi API paths.
// The %s in each string must be replaced with a Site.Name.
const (
// APIRogueAP shows your neighbors' wifis.
APIRogueAP string = "/api/s/%s/stat/rogueap"
// APIStatusPath shows Controller version.
APIStatusPath string = "/status"
// APIEventPath contains UniFi Event data.
APIEventPath string = "/api/s/%s/stat/event"
// 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"
// APIAllUserPath is Unifi Insight all previous Clients API Path.
APIAllUserPath string = "/api/s/%s/stat/alluser"
// APINetworkPath is where we get data about Unifi networks.
APINetworkPath string = "/api/s/%s/rest/networkconf"
// APIDevicePath is where we get data about Unifi devices.
APIDevicePath string = "/api/s/%s/stat/device"
// APILoginPath is Unifi Controller Login API Path.
APILoginPath string = "/api/login"
// APILoginPathNew is how we log into UDM 5.12.55+.
APILoginPathNew string = "/api/auth/login"
// APILogoutPath is how we logout from UDM.
APILogoutPath string = "/api/logout"
// APIEventPathIDS returns Intrusion Detection/Prevention Systems Events.
APIEventPathIDS string = "/api/s/%s/stat/ips/event"
// APIEventPathAlarms contains the site alarms.
APIEventPathAlarms string = "/api/s/%s/list/alarm"
// APIPrefixNew is the prefix added to the new API paths; except login. duh.
APIPrefixNew string = "/proxy/network"
// APIAnomaliesPath returns site anomalies.
APIAnomaliesPath string = "/api/s/%s/stat/anomalies"
APICommandPath string = "/api/s/%s/cmd"
APIDevMgrPath string = APICommandPath + "/devmgr"
)
// path returns the correct api path based on the new variable.
// new is based on the unifi-controller output. is it new or old output?
func (u *Unifi) path(path string) string {
if u.new {
if path == APILoginPath {
return APILoginPathNew
}
if !strings.HasPrefix(path, APIPrefixNew) && path != APILoginPathNew {
return APIPrefixNew + path
}
}
return path
}
// 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
UXGs []*UXG
}
// Config is the data passed into our library. This configures things and allows
// us to connect to a controller and write log messages. Optional SSLCert is used
// for ssl cert pinning; provide the content of a PEM to validate the server's cert.
type Config struct {
User string
Pass string
URL string
SSLCert [][]byte
ErrorLog Logger
DebugLog Logger
Timeout time.Duration // how long to wait for replies, default: forever.
VerifySSL bool
}
// 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
csrf string
fingerprints fingerprints
new bool
}
type fingerprints []string
// Contains returns true if the fingerprint is in the list.
func (f fingerprints) Contains(s string) bool {
for i := range f {
if s == f[i] {
return true
}
}
return false
}
// 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 fmt.Errorf("json unmarshal: %w", 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("%v: %w", b, ErrCannotUnmarshalFlexInt)
}
return nil
}
func (f *FlexInt) String() string {
return f.Txt
}
// 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
}
func (f *FlexBool) String() string {
return f.Txt
}

View File

@ -1,42 +0,0 @@
package unifi_test
import (
"encoding/json"
"testing"
"github.com/stretchr/testify/assert"
"github.com/unpoller/unpoller/core/unifi"
)
func TestFlexInt(t *testing.T) {
t.Parallel()
a := assert.New(t)
five, seven := 5, 7
var r struct {
Five unifi.FlexInt `json:"five"`
Seven unifi.FlexInt `json:"seven"`
Auto unifi.FlexInt `json:"auto"`
Channel unifi.FlexInt `json:"channel"`
Nil unifi.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)
}

View File

@ -1,644 +0,0 @@
package unifi
import (
"encoding/json"
"fmt"
"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 {
site *Site
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 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"`
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"`
}
// RogueAP are your neighbors access points.
type RogueAP struct {
SourceName string `json:"-"`
SiteName string `json:"-"`
ID string `json:"_id"`
ApMac string `json:"ap_mac"`
Bssid string `json:"bssid"`
SiteID string `json:"site_id"`
Age FlexInt `json:"age"`
Band string `json:"band"`
Bw FlexInt `json:"bw"`
CenterFreq FlexInt `json:"center_freq"`
Channel int `json:"channel"`
Essid string `json:"essid"`
Freq FlexInt `json:"freq"`
IsAdhoc FlexBool `json:"is_adhoc"`
IsRogue FlexBool `json:"is_rogue"`
IsUbnt FlexBool `json:"is_ubnt"`
LastSeen FlexInt `json:"last_seen"`
Noise FlexInt `json:"noise"`
Radio string `json:"radio"`
RadioName string `json:"radio_name"`
ReportTime FlexInt `json:"report_time"`
Rssi FlexInt `json:"rssi"`
RssiAge FlexInt `json:"rssi_age"`
Security string `json:"security"`
Signal FlexInt `json:"signal"`
Oui string `json:"oui"`
}
// GetRogueAPs returns RogueAPs for a list of Sites.
// Use GetRogueAPsSite if you want more control.
func (u *Unifi) GetRogueAPs(sites []*Site) ([]*RogueAP, error) {
data := []*RogueAP{}
for _, site := range sites {
response, err := u.GetRogueAPsSite(site)
if err != nil {
return data, err
}
data = append(data, response...)
}
return data, nil
}
// GetRogueAPsSite returns RogueAPs for a single Site.
func (u *Unifi) GetRogueAPsSite(site *Site) ([]*RogueAP, error) {
if site == nil || site.Name == "" {
return nil, ErrNoSiteProvided
}
u.DebugLog("Polling Controller for RogueAPs, site %s (%s)", site.SiteName, site.Desc)
var (
path = fmt.Sprintf(APIRogueAP, site.Name)
rogueaps struct {
Data []*RogueAP `json:"data"`
}
)
if err := u.GetData(path, &rogueaps, ""); err != nil {
return rogueaps.Data, err
}
for i := range rogueaps.Data {
// Add special SourceName value.
rogueaps.Data[i].SourceName = u.URL
// Add the special "Site Name" to each event. This becomes a Grafana filter somewhere.
rogueaps.Data[i].SiteName = site.SiteName
}
return rogueaps.Data, nil
}
// 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
}

View File

@ -1,53 +0,0 @@
package unifi // nolint: testpackage
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")
}

View File

@ -1,206 +0,0 @@
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 {
site *Site
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 *ConfigNetwork `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 []*EthernetTable `json:"ethernet_table"`
PortTable []Port `json:"port_table"`
EthernetOverrides []*EthernetOverrides `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"`
Temperatures []Temperature `json:"temperatures,omitempty"`
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 []*DownlinkTable `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"`
Storage []*Storage `json:"storage"`
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
}
type EthernetOverrides struct {
Ifname string `json:"ifname"`
Networkgroup string `json:"networkgroup"`
}
type EthernetTable struct {
Mac string `json:"mac"`
NumPort FlexInt `json:"num_port"`
Name string `json:"name"`
}
// NetworkTable is the list of networks on a gateway.
// Not all gateways have all features.
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"`
AutoScaleEnabled FlexBool `json:"auto_scale_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"`
ActiveDhcpLeaseCount int `json:"active_dhcp_lease_count"`
GatewayInterfaceName string `json:"gateway_interface_name"`
DPIStatsTable *DPITable `json:"dpistats_table"`
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"`
}
// Storage is hard drive into for a device with storage.
type Storage struct {
MountPoint string `json:"mount_point"`
Name string `json:"name"`
Size FlexInt `json:"size"`
Type string `json:"type"`
Used FlexInt `json:"used"`
}
type Temperature struct {
Name string `json:"name"`
Type string `json:"type"`
Value float64 `json:"value"`
}
// 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"`
}

View File

@ -1,381 +0,0 @@
// 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"
"context"
"crypto/sha256"
"crypto/tls"
"crypto/x509"
"encoding/json"
"encoding/pem"
"fmt"
"io"
"net/http"
"net/http/cookiejar"
"net/url"
"strings"
"time"
"golang.org/x/net/publicsuffix"
)
var (
ErrAuthenticationFailed = fmt.Errorf("authentication failed")
ErrInvalidStatusCode = fmt.Errorf("invalid status code from server")
ErrNoParams = fmt.Errorf("requested PUT with no parameters")
ErrInvalidSignature = fmt.Errorf("certificate signature does not match")
)
// 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(&cookiejar.Options{PublicSuffixList: publicsuffix.List})
if err != nil {
return nil, fmt.Errorf("creating cookiejar: %w", err)
}
u := newUnifi(config, jar)
for i, cert := range config.SSLCert {
p, _ := pem.Decode(cert)
u.fingerprints[i] = fmt.Sprintf("%x", sha256.Sum256(p.Bytes))
}
if err := u.checkNewStyleAPI(); err != nil {
return u, err
}
if err := u.Login(); err != nil {
return u, err
}
if err := u.GetServerData(); err != nil {
return u, fmt.Errorf("unable to get server version: %w", err)
}
return u, nil
}
func newUnifi(config *Config, jar http.CookieJar) *Unifi {
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{
Timeout: config.Timeout,
Jar: jar,
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
InsecureSkipVerify: !config.VerifySSL, // nolint: gosec
},
},
},
}
if len(config.SSLCert) > 0 {
u.fingerprints = make(fingerprints, len(config.SSLCert))
u.Client.Transport = &http.Transport{
TLSClientConfig: &tls.Config{
InsecureSkipVerify: true, // nolint: gosec
VerifyPeerCertificate: u.verifyPeerCertificate,
},
}
}
return u
}
func (u *Unifi) verifyPeerCertificate(certs [][]byte, chains [][]*x509.Certificate) error {
if len(u.fingerprints) == 0 {
return nil
}
for _, cert := range certs {
if u.fingerprints.Contains(fmt.Sprintf("%x", sha256.Sum256(cert))) {
return nil
}
}
return ErrInvalidSignature
}
// 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 fmt.Errorf("making request: %w", err)
}
defer resp.Body.Close() // we need no data here.
_, _ = io.Copy(io.Discard, resp.Body) // avoid leaking.
u.DebugLog("Requested %s: elapsed %v, returned %d bytes",
req.URL, time.Since(start).Round(time.Millisecond), resp.ContentLength)
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("(user: %s): %s (status: %s): %w",
u.User, req.URL, resp.Status, ErrAuthenticationFailed)
}
return nil
}
// Logout closes the current session.
func (u *Unifi) Logout() error {
// a post is needed for logout
_, err := u.PostJSON(APILogoutPath)
return err
}
// with the release of controller version 5.12.55 on UDM in Jan 2020 the api paths
// changed and broke this library. This function runs when `NewUnifi()` is called to
// check if this is a newer controller or not. If it is, we set new to true.
// Setting new to true makes the path() method return different (new) paths.
func (u *Unifi) checkNewStyleAPI() error {
var (
ctx = context.Background()
cancel func()
)
if u.Config.Timeout != 0 {
ctx, cancel = context.WithTimeout(ctx, u.Config.Timeout)
defer cancel()
}
u.DebugLog("Requesting %s/ to determine API paths", u.URL)
req, err := http.NewRequestWithContext(ctx, "GET", u.URL+"/", nil)
if err != nil {
return fmt.Errorf("creating request: %w", err)
}
// We can't share these cookies with other requests, so make a new client.
// Checking the return code on the first request so don't follow a redirect.
client := &http.Client{
CheckRedirect: func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
},
Transport: &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: !u.VerifySSL}, // nolint: gosec
},
}
resp, err := client.Do(req)
if err != nil {
return fmt.Errorf("making request: %w", err)
}
defer resp.Body.Close() // we need no data here.
_, _ = io.Copy(io.Discard, resp.Body) // avoid leaking.
if resp.StatusCode == http.StatusOK {
// The new version returns a "200" for a / request.
u.new = true
u.DebugLog("Using NEW UniFi controller API paths for %s", req.URL)
}
// The old version returns a "302" (to /manage) for a / request
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",
u.URL+u.path(apiPath), time.Since(start).Round(time.Millisecond), len(body))
return json.Unmarshal(body, v)
}
// PutData makes a unifi request and unmarshals the response into a provided pointer.
func (u *Unifi) PutData(apiPath string, v interface{}, params ...string) error {
start := time.Now()
body, err := u.PutJSON(apiPath, params...)
if err != nil {
return err
}
u.DebugLog("Requested %s: elapsed %v, returned %d bytes",
u.URL+u.path(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) (*http.Request, error) {
var (
req *http.Request
err error
)
switch apiPath = u.path(apiPath); params {
case "":
req, err = http.NewRequest(http.MethodGet, u.URL+apiPath, nil)
default:
req, err = http.NewRequest(http.MethodPost, u.URL+apiPath, bytes.NewBufferString(params))
}
if err != nil {
return nil, fmt.Errorf("creating request: %w", err)
}
u.setHeaders(req, params)
return req, nil
}
// UniReqPut is the Put call equivalent to UniReq.
func (u *Unifi) UniReqPut(apiPath string, params string) (*http.Request, error) {
if params == "" {
return nil, ErrNoParams
}
apiPath = u.path(apiPath)
req, err := http.NewRequest(http.MethodPut, u.URL+apiPath, bytes.NewBufferString(params)) //nolint:noctx
if err != nil {
return nil, fmt.Errorf("creating request: %w", err)
}
u.setHeaders(req, params)
return req, nil
}
// UniReqPost is the Post call equivalent to UniReq.
func (u *Unifi) UniReqPost(apiPath string, params string) (*http.Request, error) {
apiPath = u.path(apiPath)
req, err := http.NewRequest(http.MethodPost, u.URL+apiPath, bytes.NewBufferString(params)) //nolint:noctx
if err != nil {
return nil, fmt.Errorf("creating request: %w", err)
}
u.setHeaders(req, params)
return req, nil
}
// 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
}
return u.do(req)
}
// PutJSON uses a PUT call and returns the raw JSON in the same way as GetData
// Use this if you want to change data via the REST API.
func (u *Unifi) PutJSON(apiPath string, params ...string) ([]byte, error) {
req, err := u.UniReqPut(apiPath, strings.Join(params, " "))
if err != nil {
return []byte{}, err
}
return u.do(req)
}
// PostJSON uses a POST call and returns the raw JSON in the same way as GetData
// Use this if you want to change data via the REST API.
func (u *Unifi) PostJSON(apiPath string, params ...string) ([]byte, error) {
req, err := u.UniReqPost(apiPath, strings.Join(params, " "))
if err != nil {
return []byte{}, err
}
return u.do(req)
}
func (u *Unifi) do(req *http.Request) ([]byte, error) {
var (
cancel func()
ctx = context.Background()
)
if u.Config.Timeout != 0 {
ctx, cancel = context.WithTimeout(ctx, u.Config.Timeout)
defer cancel()
}
resp, err := u.Do(req.WithContext(ctx))
if err != nil {
return []byte{}, fmt.Errorf("making request: %w", err)
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return body, fmt.Errorf("reading response: %w", err)
}
// Save the returned CSRF header.
if csrf := resp.Header.Get("x-csrf-token"); csrf != "" {
u.csrf = resp.Header.Get("x-csrf-token")
}
if resp.StatusCode != http.StatusOK {
err = fmt.Errorf("%s: %s: %w", req.URL, resp.Status, ErrInvalidStatusCode)
}
return body, err
}
func (u *Unifi) setHeaders(req *http.Request, params string) {
// Add the saved CSRF header.
req.Header.Set("X-CSRF-Token", u.csrf)
req.Header.Add("Accept", "application/json")
req.Header.Add("Content-Type", "application/json; charset=utf-8")
if u.Client.Jar != nil {
parsedURL, _ := url.Parse(req.URL.String())
u.DebugLog("Requesting %s, with params: %v, cookies: %d", req.URL, params != "", len(u.Client.Jar.Cookies(parsedURL)))
} else {
u.DebugLog("Requesting %s, with params: %v,", req.URL, params != "")
}
}

View File

@ -1,100 +0,0 @@
package unifi // nolint: testpackage
import (
"io"
"net/http"
"runtime"
"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)
if runtime.GOOS == "windows" {
a.Contains(err.Error(), "connectex: No connection", "an invalid destination should produce a connection error.")
} else {
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 := io.ReadAll(r.Body)
a.Nil(err, "problem reading request body, POST parameters may be malformed")
a.EqualValues(k, string(d), "POST parameters improperly encoded")
}
func TestUniReqPut(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}}
_, err := authReq.UniReqPut(p, "")
a.NotNil(err, "empty params must produce an error")
// 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.UniReqPut(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("PUT", 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 := io.ReadAll(r.Body)
a.Nil(err, "problem reading request body, PUT parameters may be malformed")
a.EqualValues(k, string(d), "PUT parameters improperly encoded")
}
/* NOT DONE: OPEN web server, check parameters posted, more. These tests are incomplete.
a.EqualValues(`{"username": "user1","password": "pass2"}`, string(post_params),
"user/pass json parameters improperly encoded")
*/

View File

@ -1,71 +0,0 @@
package unifi
import (
"fmt"
"strings"
)
// GetUsers returns a response full of clients that connected to the UDM within the provided amount of time
// using the insight historical connection data set.
func (u *Unifi) GetUsers(sites []*Site, hours int) ([]*User, error) {
data := make([]*User, 0)
for _, site := range sites {
var (
response struct {
Data []*User `json:"data"`
}
params = fmt.Sprintf(`{ "type": "all:", "conn": "all", "within":%d }`, hours)
)
u.DebugLog("Polling Controller, retrieving UniFi Users, site %s ", site.SiteName)
clientPath := fmt.Sprintf(APIAllUserPath, site.Name)
if err := u.GetData(clientPath, &response, params); 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.SiteName
// Fix name and hostname fields. Sometimes one or the other is blank.
response.Data[i].Hostname = strings.TrimSpace(pick(d.Hostname, d.Name, d.Mac))
response.Data[i].Name = strings.TrimSpace(pick(d.Name, d.Hostname))
}
data = append(data, response.Data...)
}
return data, nil
}
// User defines the metadata available for previously connected clients.
type User struct {
SourceName string `json:"-"`
SiteName string `json:"-"`
ID string `json:"_id"`
Mac string `json:"mac"`
SiteID string `json:"site_id"`
Oui string `json:"oui,omitempty"`
IsGuest bool `json:"is_guest"`
FirstSeen FlexInt `json:"first_seen,omitempty"`
LastSeen FlexInt `json:"last_seen,omitempty"`
IsWired bool `json:"is_wired,omitempty"`
Hostname string `json:"hostname,omitempty"`
Duration FlexInt `json:"duration,omitempty"`
TxBytes FlexInt `json:"tx_bytes,omitempty"`
TxPackets FlexInt `json:"tx_packets,omitempty"`
RxBytes FlexInt `json:"rx_bytes,omitempty"`
RxPackets FlexInt `json:"rx_packets,omitempty"`
WifiTxAttempts FlexInt `json:"wifi_tx_attempts,omitempty"`
TxRetries FlexInt `json:"tx_retries,omitempty"`
UsergroupID string `json:"usergroup_id,omitempty"`
Name string `json:"name,omitempty"`
Note string `json:"note,omitempty"`
Noted FlexBool `json:"noted,omitempty"`
Blocked FlexBool `json:"blocked,omitempty"`
DevIDOverride FlexInt `json:"dev_id_override,omitempty"`
FingerprintOverride FlexBool `json:"fingerprint_override,omitempty"`
}

View File

@ -1,252 +0,0 @@
package unifi
import (
"encoding/json"
"time"
)
// USG represents all the data from the Ubiquiti Controller for a Unifi Security Gateway.
type USG struct {
site *Site
SourceName string `json:"-"`
ID string `json:"_id"`
Adopted FlexBool `json:"adopted"`
Cfgversion string `json:"cfgversion"`
ConfigNetwork *ConfigNetwork `json:"config_network"`
EthernetTable []*EthernetTable `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 []*EthernetOverrides `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"`
Temperatures []Temperature `json:"temperatures,omitempty"`
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 []*Port `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"`
Media string `json:"media"`
PortIdx FlexInt `json:"port_idx"`
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"`
RxRate FlexInt `json:"rx_rate"`
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"`
TxRate FlexInt `json:"tx_rate"`
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"` // may be deprecated
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"`
RxRate FlexInt `json:"rx_rate"`
Speed FlexInt `json:"speed"`
SpeedCaps FlexInt `json:"speed_caps"`
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"`
TxRate FlexInt `json:"tx_rate"`
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"`
Server *SpeedtestServer `json:"server"`
SourceInterface string `json:"source_interface"`
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"`
}
type SpeedtestServer struct {
Cc string `json:"cc"`
City string `json:"city"`
Country string `json:"country"`
Lat FlexInt `json:"lat"`
Lon FlexInt `json:"lon"`
Provider string `json:"provider"`
ProviderURL string `json:"provider_url"`
}
// ConfigNetwork comes from gateways.
type ConfigNetwork struct {
Type string `json:"type"`
IP string `json:"ip"`
}
// SystemStats is system info for a UDM, USG, USW.
type SystemStats struct {
CPU FlexInt `json:"cpu"`
Mem FlexInt `json:"mem"`
Uptime FlexInt `json:"uptime"`
// This exists on at least USG4, may others, maybe not.
// {"Board (CPU)":"51 C","Board (PHY)":"51 C","CPU":"72 C","PHY":"77 C"}
Temps map[string]string `json:"temps,omitempty"`
}
// 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"`
LanRxErrors FlexInt `json:"lan-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
}

View File

@ -1,60 +0,0 @@
package unifi // nolint: testpackage
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")
}

View File

@ -1,400 +0,0 @@
package unifi
import (
"encoding/json"
"time"
)
// USW represents all the data from the Ubiquiti Controller for a Unifi Switch.
type USW struct {
site *Site
SourceName string `json:"-"`
SiteName string `json:"-"`
ID string `json:"_id"`
Adopted FlexBool `json:"adopted"`
BoardRev FlexInt `json:"board_rev"`
Cfgversion string `json:"cfgversion"`
ConfigNetwork *ConfigNetwork `json:"config_network"`
Dot1XPortctrlEnabled FlexBool `json:"dot1x_portctrl_enabled"`
EthernetTable []*EthernetTable `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 *SwitchCaps `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 []*DownlinkTable `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"`
}
type SwitchCaps struct {
FeatureCaps FlexInt `json:"feature_caps"`
MaxMirrorSessions FlexInt `json:"max_mirror_sessions"`
MaxAggregateSessions FlexInt `json:"max_aggregate_sessions"`
}
// MacTable is a newer feature on some switched ports.
type MacTable struct {
Age int64 `json:"age"`
Authorized FlexBool `json:"authorized"`
Hostname string `json:"hostname"`
IP string `json:"ip"`
LastReachable int64 `json:"lastReachable"`
Mac string `json:"mac"`
}
// Port is a physical connection on a USW or Gateway.
// Not every port has the same capabilities.
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"`
MacTable []MacTable `json:"mac_table,omitempty"`
Jumbo FlexBool `json:"jumbo,omitempty"`
Masked FlexBool `json:"masked"`
Media string `json:"media"`
Name string `json:"name"`
NetworkName string `json:"network_name,omitempty"`
Netmask string `json:"netmask,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 PortDelta `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"`
RxRate FlexInt `json:"rx_rate,omitempty"`
Satisfaction FlexInt `json:"satisfaction,omitempty"`
SatisfactionReason FlexInt `json:"satisfaction_reason"`
SFPCompliance string `json:"sfp_compliance"`
SFPCurrent FlexInt `json:"sfp_current"`
SFPFound FlexBool `json:"sfp_found"`
SFPPart string `json:"sfp_part"`
SFPRev string `json:"sfp_rev"`
SFPRxfault FlexBool `json:"sfp_rxfault"`
SFPRxpower FlexInt `json:"sfp_rxpower"`
SFPSerial string `json:"sfp_serial"`
SFPTemperature FlexInt `json:"sfp_temperature"`
SFPTxfault FlexBool `json:"sfp_txfault"`
SFPTxpower FlexInt `json:"sfp_txpower"`
SFPVendor string `json:"sfp_vendor"`
SFPVoltage FlexInt `json:"sfp_voltage"`
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"`
TxRate FlexInt `json:"tx_rate,omitempty"`
Type string `json:"type,omitempty"`
Up FlexBool `json:"up"`
}
// PortDelta is part of a Port.
type PortDelta struct {
TimeDelta FlexInt `json:"time_delta"`
TimeDeltaActivity FlexInt `json:"time_delta_activity"`
}
// 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
}

View File

@ -1,75 +0,0 @@
package unifi // nolint: testpackage
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")
}

View File

@ -1,158 +0,0 @@
package unifi
// UXG represents all the data from the Ubiquiti Controller for a UniFi 10Gb Gateway.
// The UDM shares several structs/type-data with USW and USG.
type UXG struct {
site *Site
SourceName string `json:"-"`
SiteName string `json:"-"`
ID string `json:"_id"`
IP string `json:"ip"`
Mac string `json:"mac"`
Model string `json:"model"`
ModelInLts FlexBool `json:"model_in_lts"`
ModelInEol FlexBool `json:"model_in_eol"`
Type string `json:"type"`
Version string `json:"version"`
Adopted FlexBool `json:"adopted"`
SiteID string `json:"site_id"`
Cfgversion string `json:"cfgversion"`
SyslogKey string `json:"syslog_key"`
ConfigNetwork *ConfigNetwork `json:"config_network"`
SetupID string `json:"setup_id"`
LicenseState string `json:"license_state"`
ConfigNetworkLan *ConfigNetworkLan `json:"config_network_lan"`
InformURL string `json:"inform_url"`
InformIP string `json:"inform_ip"`
RequiredVersion string `json:"required_version"`
KernelVersion string `json:"kernel_version"`
Architecture string `json:"architecture"`
BoardRev FlexInt `json:"board_rev"`
ManufacturerID FlexInt `json:"manufacturer_id"`
Internet FlexBool `json:"internet"`
ModelIncompatible FlexBool `json:"model_incompatible"`
EthernetTable []*EthernetTable `json:"ethernet_table"`
PortTable []Port `json:"port_table"`
EthernetOverrides []*EthernetOverrides `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 *SwitchCaps `json:"switch_caps"`
HasFan FlexBool `json:"has_fan"`
HasTemperature FlexBool `json:"has_temperature"`
Temperatures []Temperature `json:"temperatures"`
Storage []*Storage `json:"storage"`
RulesetInterfaces interface{} `json:"ruleset_interfaces"`
ConnectedAt FlexInt `json:"connected_at"`
ProvisionedAt FlexInt `json:"provisioned_at"`
LedOverride string `json:"led_override"`
LedOverrideColor string `json:"led_override_color"`
LedOverrideColorBrightness FlexInt `json:"led_override_color_brightness"`
OutdoorModeOverride string `json:"outdoor_mode_override"`
LcmBrightnessOverride FlexBool `json:"lcm_brightness_override"`
LcmIdleTimeoutOverride FlexBool `json:"lcm_idle_timeout_override"`
Name string `json:"name"`
Unsupported FlexBool `json:"unsupported"`
UnsupportedReason FlexInt `json:"unsupported_reason"`
Serial string `json:"serial"`
HashID string `json:"hash_id"`
TwoPhaseAdopt FlexBool `json:"two_phase_adopt"`
DeviceID string `json:"device_id"`
State FlexInt `json:"state"`
StartDisconnectedMillis FlexInt `json:"start_disconnected_millis"`
UpgradeState FlexInt `json:"upgrade_state"`
StartConnectedMillis FlexInt `json:"start_connected_millis"`
LastSeen FlexInt `json:"last_seen"`
Uptime FlexInt `json:"uptime"`
UnderscoreUptime FlexInt `json:"_uptime"`
Locating FlexBool `json:"locating"`
SysStats SysStats `json:"sys_stats"`
SystemStats SystemStats `json:"system-stats"`
GuestKicks FlexInt `json:"guest_kicks"`
GuestToken string `json:"guest_token"`
UptimeStats map[string]*UptimeStats `json:"uptime_stats"`
Overheating FlexBool `json:"overheating"`
GeoInfo map[string]*GeoInfo `json:"geo_info"`
LedState *LedState `json:"led_state"`
SpeedtestStatus SpeedtestStatus `json:"speedtest-status"`
SpeedtestStatusSaved FlexBool `json:"speedtest-status-saved"`
Wan1 Wan `json:"wan1"`
Wan2 Wan `json:"wan2"`
Uplink Uplink `json:"uplink"`
DownlinkTable []*DownlinkTable `json:"downlink_table"`
NetworkTable NetworkTable `json:"network_table"`
KnownCfgversion string `json:"known_cfgversion"`
ConnectRequestIP string `json:"connect_request_ip"`
ConnectRequestPort string `json:"connect_request_port"`
NextInterval FlexInt `json:"next_interval"`
NextHeartbeatAt FlexInt `json:"next_heartbeat_at"`
ConsideredLostAt FlexInt `json:"considered_lost_at"`
Stat *UXGStat `json:"stat"`
TxBytes FlexInt `json:"tx_bytes"`
RxBytes FlexInt `json:"rx_bytes"`
Bytes FlexInt `json:"bytes"`
NumSta FlexInt `json:"num_sta"`
WlanNumSta FlexInt `json:"wlan-num_sta"`
LanNumSta FlexInt `json:"lan-num_sta"`
UserWlanNumSta FlexInt `json:"user-wlan-num_sta"`
UserLanNumSta FlexInt `json:"user-lan-num_sta"`
UserNumSta FlexInt `json:"user-num_sta"`
GuestWlanNumSta FlexInt `json:"guest-wlan-num_sta"`
GuestLanNumSta FlexInt `json:"guest-lan-num_sta"`
GuestNumSta FlexInt `json:"guest-num_sta"`
NumDesktop FlexInt `json:"num_desktop"`
NumMobile FlexInt `json:"num_mobile"`
NumHandheld FlexInt `json:"num_handheld"`
}
// ConfigNetworkLan is part of a UXG, maybe others.
type ConfigNetworkLan struct {
DhcpEnabled FlexBool `json:"dhcp_enabled"`
Vlan int `json:"vlan"`
}
// DownlinkTable is part of a UXG and UDM output.
type DownlinkTable struct {
PortIdx FlexInt `json:"port_idx"`
Speed FlexInt `json:"speed"`
FullDuplex FlexBool `json:"full_duplex"`
Mac string `json:"mac"`
}
// LedState is incuded with newer devices.
type LedState struct {
Pattern string `json:"pattern"`
Tempo FlexInt `json:"tempo"`
}
// GeoInfo is incuded with certain devices.
type GeoInfo struct {
Accuracy FlexInt `json:"accuracy"`
Address string `json:"address"`
Asn FlexInt `json:"asn"`
City string `json:"city"`
ContinentCode string `json:"continent_code"`
CountryCode string `json:"country_code"`
CountryName string `json:"country_name"`
IspName string `json:"isp_name"`
IspOrganization string `json:"isp_organization"`
Latitude FlexInt `json:"latitude"`
Longitude FlexInt `json:"longitude"`
Timezone string `json:"timezone"`
}
// UptimeStats is incuded with certain devices.
type UptimeStats struct {
Availability FlexInt `json:"availability"`
LatencyAverage FlexInt `json:"latency_average"`
TimePeriod FlexInt `json:"time_period"`
}
// UXGStat holds the "stat" data for a 10Gb gateway.
type UXGStat struct {
*Gw `json:"gw"`
*Sw `json:"sw"`
}

View File

@ -5,7 +5,7 @@ import (
"strconv" "strconv"
"time" "time"
"github.com/unpoller/unpoller/core/unifi" "github.com/unpoller/unifi"
) )
const ( const (

View File

@ -1,7 +1,7 @@
package datadogunifi package datadogunifi
import ( import (
"github.com/unpoller/unpoller/core/unifi" "github.com/unpoller/unifi"
) )
// batchClient generates Unifi Client datapoints for Datadog. // batchClient generates Unifi Client datapoints for Datadog.

View File

@ -7,8 +7,8 @@ import (
"time" "time"
"github.com/DataDog/datadog-go/statsd" "github.com/DataDog/datadog-go/statsd"
"github.com/unpoller/unifi"
"github.com/unpoller/unpoller/core/poller" "github.com/unpoller/unpoller/core/poller"
"github.com/unpoller/unpoller/core/unifi"
"golift.io/cnfg" "golift.io/cnfg"
) )
@ -27,8 +27,8 @@ type Config struct {
// Save data for dead ports? ie. ports that are down or disabled. // Save data for dead ports? ie. ports that are down or disabled.
DeadPorts bool `json:"dead_ports,omitempty" toml:"dead_ports,omitempty" xml:"dead_ports,omitempty" yaml:"dead_ports,omitempty"` DeadPorts bool `json:"dead_ports,omitempty" toml:"dead_ports,omitempty" xml:"dead_ports,omitempty" yaml:"dead_ports,omitempty"`
// Disable when true disables this output plugin // Enable when true, enables this output plugin
Disable *bool `json:"disable" toml:"disable" xml:"disable,attr" yaml:"disable"` Enable *bool `json:"enable" toml:"enable" xml:"enable,attr" yaml:"enable"`
// Address determines how to talk to the Datadog agent // Address determines how to talk to the Datadog agent
Address string `json:"address" toml:"address" xml:"address,attr" yaml:"address"` Address string `json:"address" toml:"address" xml:"address,attr" yaml:"address"`
@ -190,16 +190,12 @@ func (u *DatadogUnifi) setConfigDefaults() {
// Run runs a ticker to poll the unifi server and update Datadog. // Run runs a ticker to poll the unifi server and update Datadog.
func (u *DatadogUnifi) Run(c poller.Collect) error { func (u *DatadogUnifi) Run(c poller.Collect) error {
u.Collector = c disabled := u == nil || u.Enable == nil || !(*u.Enable) || u.Config == nil
disabled := u.Disable == nil || *u.Disable
if disabled { if disabled {
u.LogDebugf("Datadog config is disabled, output is disabled.") u.LogDebugf("Datadog config is disabled, output is disabled.")
return nil return nil
} }
if u.Config == nil && !disabled { u.Collector = c
u.LogErrorf("DataDog config is missing and is not disabled: Datadog output is disabled!")
return nil
}
u.Logf("Datadog is configured.") u.Logf("Datadog is configured.")
u.setConfigDefaults() u.setConfigDefaults()

View File

@ -5,7 +5,7 @@ import (
"strconv" "strconv"
"time" "time"
"github.com/unpoller/unpoller/core/unifi" "github.com/unpoller/unifi"
) )
// These constants are used as names for printed/logged counters. // These constants are used as names for printed/logged counters.

View File

@ -1,7 +1,7 @@
package datadogunifi package datadogunifi
import ( import (
"github.com/unpoller/unpoller/core/unifi" "github.com/unpoller/unifi"
) )
// reportSite generates Unifi Sites' datapoints for Datadog. // reportSite generates Unifi Sites' datapoints for Datadog.

View File

@ -1,7 +1,7 @@
package datadogunifi package datadogunifi
import ( import (
"github.com/unpoller/unpoller/core/unifi" "github.com/unpoller/unifi"
) )
// uapT is used as a name for printed/logged counters. // uapT is used as a name for printed/logged counters.

View File

@ -4,7 +4,7 @@ import (
"strconv" "strconv"
"strings" "strings"
"github.com/unpoller/unpoller/core/unifi" "github.com/unpoller/unifi"
) )
// udmT is used as a name for printed/logged counters. // udmT is used as a name for printed/logged counters.

View File

@ -1,7 +1,7 @@
package datadogunifi package datadogunifi
import ( import (
"github.com/unpoller/unpoller/core/unifi" "github.com/unpoller/unifi"
) )
// usgT is used as a name for printed/logged counters. // usgT is used as a name for printed/logged counters.

View File

@ -1,7 +1,7 @@
package datadogunifi package datadogunifi
import ( import (
"github.com/unpoller/unpoller/core/unifi" "github.com/unpoller/unifi"
) )
// uswT is used as a name for printed/logged counters. // uswT is used as a name for printed/logged counters.

View File

@ -1,7 +1,7 @@
package datadogunifi package datadogunifi
import ( import (
"github.com/unpoller/unpoller/core/unifi" "github.com/unpoller/unifi"
) )
// uxgT is used as a name for printed/logged counters. // uxgT is used as a name for printed/logged counters.

View File

@ -3,7 +3,7 @@ package influxunifi
import ( import (
"time" "time"
"github.com/unpoller/unpoller/core/unifi" "github.com/unpoller/unifi
) )
const ( const (

View File

@ -1,7 +1,7 @@
package influxunifi package influxunifi
import ( import (
"github.com/unpoller/unpoller/core/unifi" "github.com/unpoller/unifi"
) )
// batchClient generates Unifi Client datapoints for InfluxDB. // batchClient generates Unifi Client datapoints for InfluxDB.

View File

@ -3,7 +3,7 @@ package influxunifi
import ( import (
"time" "time"
"github.com/unpoller/unpoller/core/unifi" "github.com/unpoller/unifi"
) )
// These constants are used as names for printed/logged counters. // These constants are used as names for printed/logged counters.

View File

@ -12,8 +12,8 @@ import (
"time" "time"
influx "github.com/influxdata/influxdb1-client/v2" influx "github.com/influxdata/influxdb1-client/v2"
"github.com/unpoller/unifi"
"github.com/unpoller/unpoller/core/poller" "github.com/unpoller/unpoller/core/poller"
"github.com/unpoller/unpoller/core/unifi"
"github.com/unpoller/unpoller/core/webserver" "github.com/unpoller/unpoller/core/webserver"
"golift.io/cnfg" "golift.io/cnfg"
) )

View File

@ -1,7 +1,7 @@
package influxunifi package influxunifi
import ( import (
"github.com/unpoller/unpoller/core/unifi" "github.com/unpoller/unifi"
) )
// batchSite generates Unifi Sites' datapoints for InfluxDB. // batchSite generates Unifi Sites' datapoints for InfluxDB.

View File

@ -1,7 +1,7 @@
package influxunifi package influxunifi
import ( import (
"github.com/unpoller/unpoller/core/unifi" "github.com/unpoller/unifi"
) )
// uapT is used as a name for printed/logged counters. // uapT is used as a name for printed/logged counters.

View File

@ -4,7 +4,7 @@ import (
"strconv" "strconv"
"strings" "strings"
"github.com/unpoller/unpoller/core/unifi" "github.com/unpoller/unifi"
) )
// udmT is used as a name for printed/logged counters. // udmT is used as a name for printed/logged counters.

View File

@ -1,7 +1,7 @@
package influxunifi package influxunifi
import ( import (
"github.com/unpoller/unpoller/core/unifi" "github.com/unpoller/unifi"
) )
// usgT is used as a name for printed/logged counters. // usgT is used as a name for printed/logged counters.

View File

@ -1,7 +1,7 @@
package influxunifi package influxunifi
import ( import (
"github.com/unpoller/unpoller/core/unifi" "github.com/unpoller/unifi"
) )
// uswT is used as a name for printed/logged counters. // uswT is used as a name for printed/logged counters.

View File

@ -1,7 +1,7 @@
package influxunifi package influxunifi
import ( import (
"github.com/unpoller/unpoller/core/unifi" "github.com/unpoller/unifi"
) )
// uxgT is used as a name for printed/logged counters. // uxgT is used as a name for printed/logged counters.

View File

@ -4,7 +4,7 @@ import (
"fmt" "fmt"
"time" "time"
"github.com/unpoller/unpoller/core/unifi" "github.com/unpoller/unifi"
"github.com/unpoller/unpoller/core/webserver" "github.com/unpoller/unpoller/core/webserver"
) )

View File

@ -7,8 +7,8 @@ import (
"strings" "strings"
"time" "time"
"github.com/unpoller/unifi"
"github.com/unpoller/unpoller/core/poller" "github.com/unpoller/unpoller/core/poller"
"github.com/unpoller/unpoller/core/unifi"
) )
var ErrScrapeFilterMatchFailed = fmt.Errorf("scrape filter match failed, and filter is not http URL") var ErrScrapeFilterMatchFailed = fmt.Errorf("scrape filter match failed, and filter is not http URL")

View File

@ -9,8 +9,8 @@ import (
"sync" "sync"
"time" "time"
"github.com/unpoller/unifi"
"github.com/unpoller/unpoller/core/poller" "github.com/unpoller/unpoller/core/poller"
"github.com/unpoller/unpoller/core/unifi"
) )
// PluginName is the name of this input plugin. // PluginName is the name of this input plugin.

View File

@ -6,8 +6,8 @@ import (
"fmt" "fmt"
"strings" "strings"
"github.com/unpoller/unifi"
"github.com/unpoller/unpoller/core/poller" "github.com/unpoller/unpoller/core/poller"
"github.com/unpoller/unpoller/core/unifi"
"github.com/unpoller/unpoller/core/webserver" "github.com/unpoller/unpoller/core/webserver"
) )

View File

@ -5,7 +5,7 @@ import (
"strconv" "strconv"
"time" "time"
"github.com/unpoller/unpoller/core/unifi" "github.com/unpoller/unifi"
"github.com/unpoller/unpoller/core/webserver" "github.com/unpoller/unpoller/core/webserver"
) )

View File

@ -5,8 +5,8 @@ import (
"strings" "strings"
"time" "time"
"github.com/unpoller/unifi"
"github.com/unpoller/unpoller/core/poller" "github.com/unpoller/unpoller/core/poller"
"github.com/unpoller/unpoller/core/unifi"
) )
// LogStream contains a stream of logs (like a log file). // LogStream contains a stream of logs (like a log file).

View File

@ -3,7 +3,7 @@ package lokiunifi
import ( import (
"strconv" "strconv"
"github.com/unpoller/unpoller/core/unifi" "github.com/unpoller/unifi"
) )
const typeAlarm = "Alarm" const typeAlarm = "Alarm"

View File

@ -3,7 +3,7 @@ package lokiunifi
import ( import (
"strconv" "strconv"
"github.com/unpoller/unpoller/core/unifi" "github.com/unpoller/unifi"
) )
const typeAnomaly = "Anomaly" const typeAnomaly = "Anomaly"

View File

@ -3,7 +3,7 @@ package lokiunifi
import ( import (
"strconv" "strconv"
"github.com/unpoller/unpoller/core/unifi" "github.com/unpoller/unifi"
) )
const typeEvent = "Event" const typeEvent = "Event"

View File

@ -3,7 +3,7 @@ package lokiunifi
import ( import (
"strconv" "strconv"
"github.com/unpoller/unpoller/core/unifi" "github.com/unpoller/unifi"
) )
const typeIDS = "IDS" const typeIDS = "IDS"

View File

@ -2,7 +2,7 @@ package promunifi
import ( import (
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
"github.com/unpoller/unpoller/core/unifi" "github.com/unpoller/unifi"
) )
type uclient struct { type uclient struct {

View File

@ -12,8 +12,8 @@ import (
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp" "github.com/prometheus/client_golang/prometheus/promhttp"
promver "github.com/prometheus/common/version" promver "github.com/prometheus/common/version"
"github.com/unpoller/unifi"
"github.com/unpoller/unpoller/core/poller" "github.com/unpoller/unpoller/core/poller"
"github.com/unpoller/unpoller/core/unifi"
"github.com/unpoller/unpoller/core/webserver" "github.com/unpoller/unpoller/core/webserver"
"golift.io/version" "golift.io/version"
) )

View File

@ -2,7 +2,7 @@ package promunifi
import ( import (
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
"github.com/unpoller/unpoller/core/unifi" "github.com/unpoller/unifi"
) )
type site struct { type site struct {

View File

@ -2,7 +2,7 @@ package promunifi
import ( import (
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
"github.com/unpoller/unpoller/core/unifi" "github.com/unpoller/unifi"
) )
type uap struct { type uap struct {

View File

@ -2,7 +2,7 @@ package promunifi
import ( import (
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
"github.com/unpoller/unpoller/core/unifi" "github.com/unpoller/unifi"
) )
// These are shared by all four device types: UDM, UAP, USG, USW. // These are shared by all four device types: UDM, UAP, USG, USW.

View File

@ -5,7 +5,7 @@ import (
"strings" "strings"
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
"github.com/unpoller/unpoller/core/unifi" "github.com/unpoller/unifi"
) )
type usg struct { type usg struct {

View File

@ -2,7 +2,7 @@ package promunifi
import ( import (
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
"github.com/unpoller/unpoller/core/unifi" "github.com/unpoller/unifi"
) )
type usw struct { type usw struct {

View File

@ -1,7 +1,7 @@
package promunifi package promunifi
import ( import (
"github.com/unpoller/unpoller/core/unifi" "github.com/unpoller/unifi"
) )
// exportUXG is a collection of stats from USG and USW. It has no unique stats. // exportUXG is a collection of stats from USG and USW. It has no unique stats.