Files
Cody Lee 65ad210f20 feat: add UniFi Protect device metrics (closes #1015)
Collects Protect device data (sensors, cameras, lights, bridges, link
stations, NVR) via the official Integration API and exports it through
Prometheus, InfluxDB, and DataDog. Opt-in via save_protect_devices,
gated by protect_api_key.

Bumps github.com/unpoller/unifi to v6, which added the Protect API
client (breaking change: FlexInt/FlexBool/FlexFloat replace nullable
pointers).
2026-08-19 15:14:43 -05:00

94 lines
1.8 KiB
Go

package influxunifi
import (
"github.com/unpoller/unifi/v6"
)
// batchTopology generates topology datapoints for InfluxDB.
func (u *InfluxUnifi) batchTopology(r report, t *unifi.Topology) {
if t == nil {
return
}
var (
devices int
clients int
wired int
wireless int
fullDuplex int
)
unknownSwitch := 0
if t.HasUnknownSwitch {
unknownSwitch = 1
}
for i := range t.Vertices {
switch t.Vertices[i].Type {
case "DEVICE":
devices++
case "CLIENT":
clients++
}
}
for i := range t.Edges {
e := &t.Edges[i]
edgeTags := map[string]string{
"uplink_mac": e.UplinkMac,
"downlink_mac": e.DownlinkMac,
"link_type": e.Type,
"site_name": t.SiteName,
"source": t.SourceName,
}
switch e.Type {
case "WIRED":
wired++
if e.Duplex == "FULL_DUPLEX" {
fullDuplex++
}
edgeFields := map[string]any{
"rate_mbps": e.RateMbps.Val,
}
r.send(&metric{Table: "topology_edge", Tags: edgeTags, Fields: edgeFields})
case "WIRELESS":
wireless++
edgeTags["essid"] = e.Essid
edgeTags["radio_band"] = e.RadioBand
edgeTags["protocol"] = e.Protocol
edgeFields := map[string]any{
"experience_score": e.ExperienceScore.Val,
"channel": e.Channel.Val,
}
r.send(&metric{Table: "topology_edge", Tags: edgeTags, Fields: edgeFields})
}
}
summaryTags := map[string]string{
"site_name": t.SiteName,
"source": t.SourceName,
}
summaryFields := map[string]any{
"vertices_total": len(t.Vertices),
"edges_total": len(t.Edges),
"devices_total": devices,
"clients_total": clients,
"connections_wired": wired,
"connections_wireless": wireless,
"wired_full_duplex": fullDuplex,
"has_unknown_switch": unknownSwitch,
}
r.send(&metric{Table: "topology_summary", Tags: summaryTags, Fields: summaryFields})
}