Files
unpoller_unpoller/pkg/promunifi/uci.go
T
ek-docker-images 6d1b1361fc feat(promunifi): export DOCSIS CI state metrics for UCI devices
Adds two new Prometheus metrics for UCI (UniFi Cable Internet) devices:

- unpoller_device_ci_state_operational (gauge): 1 if DOCSIS CI state ==
  "Operational", else 0. Suitable for alerting on cable link health.

- unpoller_device_ci_state_info (gauge=1, info-style): exposes the full
  DOCSIS CI state table as labels (ci_state, ci_sw_dl_status, ci_mac,
  ci_version, ci_mode) for diagnostic dashboards.

The controller does expose a top-level `internet` boolean on UCI
devices, but it is not a reliable WAN-reachability signal — it stays
false even when the cable link is fully operational and the upstream
WAN is up. The UCI is a cable bridge with no independent internet-
reachability probe; real WAN health lives on the upstream gateway
(e.g. UDM wan1.up).

The ci_state field from ci_state_table IS reported reliably and is the
correct source-of-truth for DOCSIS link health.

Verified on a UCI in Operational state (ci_mode=D3.1).
2026-05-24 15:58:25 -07:00

69 lines
2.0 KiB
Go

package promunifi
import (
"github.com/unpoller/unifi/v5"
)
// exportUCI is a collection of stats from UCI.
func (u *promUnifi) exportUCI(r report, d *unifi.UCI) {
if !d.Adopted.Val || d.Locating.Val {
return
}
var sw *unifi.Sw
if d.Stat != nil {
sw = d.Stat.Sw
}
baseLabels := []string{d.Type, d.SiteName, d.Name, d.SourceName}
baseInfoLabels := []string{d.Version, d.Model, d.Serial, d.Mac, d.IP, d.ID}
u.exportWithTags(r, d.Tags, func(tagLabels []string) {
tag := tagLabels[0]
labels := append(baseLabels, tag)
infoLabels := append(baseInfoLabels, tag)
// Shared data (all devices do this).
u.exportBYTstats(r, labels, d.TxBytes, d.RxBytes)
if d.SysStats != nil && d.SystemStats != nil {
u.exportSYSstats(r, labels, *d.SysStats, *d.SystemStats)
}
// Switch Data
u.exportUSWstats(r, labels, sw)
// Dream Machine System Data.
r.send([]*metric{
{u.Device.Info, gauge, 1.0, append(baseLabels, infoLabels...)},
{u.Device.Uptime, gauge, d.Uptime, labels},
})
// DOCSIS / Cable Internet state.
//
// The controller exposes a top-level `internet` boolean on UCI devices,
// but it is not a reliable WAN-reachability signal — it stays false
// even when the cable link is fully up and traffic is flowing. The
// UCI is a cable bridge with no independent internet-reachability
// check; real WAN health lives on the upstream gateway (e.g. UDM
// `wan1.up`).
//
// Instead, derive an operational gauge from the DOCSIS CI state,
// which IS reported reliably by the controller. The full state
// string is also exposed as a label on `*_ci_state_info`.
if d.CiStateTable != nil {
r.send([]*metric{
{
u.Device.CiStateOperational, gauge,
d.CiStateTable.CIState == "Operational",
labels,
},
{
u.Device.CiStateInfo, gauge, 1.0,
append(labels, d.CiStateTable.CIState, d.CiStateTable.CISwDlStatus,
d.CiStateTable.CIMac, d.CiStateTable.CIVersion, d.CiStateTable.CIMode),
},
})
}
})
}