Commit Graph
5066 Commits
Author SHA1 Message Date
Cody Lee b479f9bcd2 Merge pull request #1089 from unpoller/dependabot/go_modules/all-df48bebb66 2026-09-14 13:23:33 -04:00
dependabot[bot] 0f4d1be9c7 build(deps): bump the all group across 1 directory with 4 updates
Bumps the all group with 2 updates in the / directory: [golang.org/x/crypto](https://github.com/golang/crypto) and [golift.io/cnfg](https://github.com/golift/cnfg).


Updates `golang.org/x/crypto` from 0.56.0 to 0.57.0
- [Commits](https://github.com/golang/crypto/compare/v0.56.0...v0.57.0)

Updates `golang.org/x/sync` from 0.22.0 to 0.23.0
- [Commits](https://github.com/golang/sync/compare/v0.22.0...v0.23.0)

Updates `golang.org/x/term` from 0.45.0 to 0.46.0
- [Commits](https://github.com/golang/term/compare/v0.45.0...v0.46.0)

Updates `golift.io/cnfg` from 0.2.5 to 0.4.0
- [Release notes](https://github.com/golift/cnfg/releases)
- [Commits](https://github.com/golift/cnfg/compare/v0.2.5...v0.4.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-version: 0.57.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: all
- dependency-name: golang.org/x/sync
  dependency-version: 0.23.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: all
- dependency-name: golang.org/x/term
  dependency-version: 0.46.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: all
- dependency-name: golift.io/cnfg
  dependency-version: 0.4.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: all
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-09-14 14:59:13 +00:00
Cody Lee 7827d30380 Merge pull request #1087 from NotAFlightRisk/fix/health-check-ipv6-http-listen v5.2.5 2026-09-11 19:46:59 -04:00
Iain 4da7cd5936 fix(promunifi): accept a bracketed IPv6 http_listen in the health check 2026-09-12 00:14:17 +01:00
Cody Lee d78d47cb93 Merge pull request #1086 from unpoller/fix/prometheus-cache-and-locate-mode
fix: honor scrape-cache disable and export locate-mode devices
2026-09-11 09:54:37 -04:00
Cody LeeandCursor 122226bc4b fix: honor scrape-cache disable and export locate-mode devices
interval=0 now turns off the Prometheus scrape cache as PR #1014
documented, and sub-15s intervals warn instead of clamping (#1083).
Adopted devices stay in Prometheus, Influx, OTel, and Datadog exports
while locate/identify is on (#1075).

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-11 08:51:32 -05:00
Cody Lee 467fbd2de1 Merge pull request #1082 from unpoller/dependabot/go_modules/google.golang.org/grpc-1.83.2 2026-09-08 23:16:21 -04:00
dependabot[bot] c2bdfd2d2b build(deps): bump google.golang.org/grpc from 1.83.1 to 1.83.2
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.83.1 to 1.83.2.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.83.1...v1.83.2)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-version: 1.83.2
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-09-09 00:23:30 +00:00
dependabot[bot] 089dda98fd build(deps): bump the all group with 4 updates (#1080)
Bumps the all group with 4 updates: [github.com/prometheus/common](https://github.com/prometheus/common), [github.com/unpoller/unifi/v6](https://github.com/unpoller/unifi), [golang.org/x/crypto](https://github.com/golang/crypto) and [github.com/prometheus/client_model](https://github.com/prometheus/client_model).


Updates `github.com/prometheus/common` from 0.70.1 to 0.71.0
- [Release notes](https://github.com/prometheus/common/releases)
- [Changelog](https://github.com/prometheus/common/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prometheus/common/compare/v0.70.1...v0.71.0)

Updates `github.com/unpoller/unifi/v6` from 6.1.0 to 6.1.2
- [Release notes](https://github.com/unpoller/unifi/releases)
- [Commits](https://github.com/unpoller/unifi/compare/v6.1.0...v6.1.2)

Updates `golang.org/x/crypto` from 0.55.0 to 0.56.0
- [Commits](https://github.com/golang/crypto/compare/v0.55.0...v0.56.0)

Updates `github.com/prometheus/client_model` from 0.6.2 to 0.6.3
- [Release notes](https://github.com/prometheus/client_model/releases)
- [Commits](https://github.com/prometheus/client_model/compare/v0.6.2...v0.6.3)

---
updated-dependencies:
- dependency-name: github.com/prometheus/common
  dependency-version: 0.71.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: all
- dependency-name: github.com/unpoller/unifi/v6
  dependency-version: 6.1.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: all
- dependency-name: golang.org/x/crypto
  dependency-version: 0.56.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: all
- dependency-name: github.com/prometheus/client_model
  dependency-version: 0.6.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: all
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-07 07:26:31 -04:00
Cody Lee 8cf64e1d78 Merge pull request #1077 from NotAFlightRisk/fix/health-check-empty-http-listen v5.2.3 v5.2.4 2026-09-02 20:40:31 -04:00
Iain e2384b8bb8 fix(promunifi): default an unset http_listen instead of failing validation 2026-09-03 01:12:20 +01:00
Cody Lee 4e3c58a79a Merge pull request #1074 from Prototype0645/fix/site-name-override-logs v5.2.2 2026-09-01 07:58:47 -04:00
Prototype0645 6c08d43b4c fix(inputunifi): apply default_site_name_override to log entries
default_site_name_override is applied in augmentMetrics, which only the
metrics path goes through. Log entries leave by collectControllerEvents,
which reads its sites straight from getFilteredSites — where the override
is deliberately not applied — so events, alarms, IDS records and system-log
entries ship with the controller's stock site name.

Four of the five site-scoped log collectors were affected; collectAnomalies
already did this inline, which is what makes the omission visible.

The consequence is worst for a poller watching several UniFi OS consoles:
each one calls its only site "default", so their log entries are
indistinguishable downstream. In Loki every stream from those consoles lands
under site_name="Default (default)" no matter which console it came from,
and no relabeling downstream can separate them again — the information is
gone by then. That is precisely the case the option was added for, and it
works for the metrics from those same consoles.

Extract the check collectAnomalies was doing into overrideSiteName and call
it from all five, so the two paths agree.

Note that only Site.Name reaches an API path; Site.SiteName is a display
name throughout the library. The override is therefore safe here, which is
what applySiteNameOverride's own comment already says ("keeping default for
API calls").
2026-09-01 09:08:13 +02:00
Cody Lee 3d70164732 Merge pull request #1073 from Prototype0645/feat/wan-status-source-label 2026-08-31 19:35:52 -04:00
Prototype0645 ca64173b35 feat(wan): label WAN interface state with its source
unpoller_wan_interface_state ships with site_name and nothing else to
identify where it came from. That is not enough: every UniFi OS console
names its only site "default", so site_name is "Default (default)" on all
of them. An instance polling several controllers emits WAN status that is
indistinguishable, and downstream attribution has to be guessed.

Observed in production before the fix: a scrape config guessing from
site_name filed a UDM's WAN state under the wrong customer. No error, no
missing series — just wrong data under someone else's name.

unifi/v6.1.0 (#244) added SourceName to WANStatus, stamped from the
controller URL in all three read paths. go.mod is already on v6.1.0, so
this only has to read the field.

Two lines, same shape as #1071 which did this for unpoller_wan_*.

Tests use the fakeReport already present in the package: every emitted
metric carries both site_name and source, the raw state is kept as a
label, and a nil status still produces nothing rather than panicking a
poll. TestExportWANStatusIsAttributed fails on master and passes here.
2026-08-31 23:42:30 +02:00
Cody Lee ad84c29f29 Merge pull request #1072 from unpoller/fix/986-remote-multi-site
fix: poll remote multi-site controllers by internalReference
v5.2.1
2026-08-31 15:17:59 -04:00
Cody LeeandCursor 8a78355e12 fix: use external test package for remote site helpers
golangci-lint testpackage requires tests in inputunifi_test.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-31 14:01:11 -04:00
Cody LeeandCursor e4c2934bdc fix: poll remote multi-site controllers by internalReference
Remote discovery stored Integration display names, so extra sites were dropped when checkSites compared them to legacy Site.Name. Use unifi v6.1.0 InternalReference instead.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-31 13:54:21 -04:00
Cody Lee 6972dfc0c2 Merge pull request #1068 from cooperlees/feat/protect-only-console v5.2.0 2026-08-31 09:20:39 -04:00
Cooper Ry LeesandClaude Opus 5 a73a2e0ab2 fix: don't send unused credentials to a Protect-only console
Found by running this against a real UNVR4 rather than only the fake one.

setDefaults fills an unset user with the placeholder "unifipoller". For a
Protect-only console that placeholder was reaching Login(), the console answered
403, and the controller died during initialisation -- the same symptom #1066 set
out to fix, one layer further in. A Protect API key and no local account is the
config an operator actually writes for a UNVR, so this was the common case, not
an edge one.

Nothing on such a console uses a session unless Protect logs are wanted: the
Integration API authenticates with the key alone. So withhold the credentials
entirely in that case, and say so in the startup summary rather than naming a
username that is never sent.

Also pins the go.mod bump to unifi v6.0.1, which is the release that carries
NewProtectClient (unpoller/unifi#240).

Verified end to end against a UNVR4 (UniFi OS 5.1.31, Protect 7.2.105): the
controller comes up, logs "Auth: Protect API key only (no session needed)",
makes no login request at all, and exports 37 unpoller_protect_* series across
9 cameras and 2 bridges with unpoller_controller_up = 1.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GVreutpEATmBjm6PBw9RjQ
2026-08-31 13:08:34 +00:00
Cooper Ry LeesandClaude Opus 5 31cc5a0f31 feat: poll Protect-only consoles via disable_network (closes #1066)
A UNVR or UNVR Pro runs UniFi Protect with no Network application installed.
UnPoller could not poll one at all: NewUnifi ends with GetServerData(), a GET of
/proxy/network/status, which such a console answers with its UniFi OS SPA HTML.
The controller entry died during initialisation and re-failed every interval,
never even printing a config summary -- while the Protect Integration API on the
same host answered every endpoint with the same key.

Set disable_network = true on that controller. It defaults to false, so nothing
about an existing config changes.

The Protect collectors were already complete and already not site-scoped; three
things stood between them and a Protect-only console:

  - getUnifi now calls unifi.NewProtectClient, which skips the Network probe and
    validates the Protect Integration API instead (unpoller/unifi#240).

  - pollController aborted on getFilteredSites long before reaching
    collectProtect, and collectControllerEvents did the same before
    collectProtectLogs. The Network pass is extracted into pollNetwork and
    skipped wholesale; the event collector list reduces to collectProtectLogs,
    the only site-independent one.

  - Metrics counted a poll successful only if it produced devices or clients. A
    Protect-only console produces neither, so a filtered scrape of one -- the
    Prometheus per-target path -- fell through to the dynamic-controller branch
    and reported ErrDynamicLookupsDisabled despite a successful collection.
    ProtectDevices now counts too.

Two smaller things worth calling out for reviewers:

  - extractDevices dereferenced metrics.Devices unguarded. That was already a
    latent panic; skipping the Network pass makes it reachable, so it is fixed
    here rather than left for the first person to hit it.

  - RawMetrics answers the raw-path kind for these consoles and rejects the
    site-scoped kinds with ErrNetworkDisabled. Returning an empty result would
    read as "this console has no devices" rather than "wrong question".

warnProtectOnly logs an error, without failing the controller, for the two
configurations that can never collect anything: disable_network with neither
Protect save flag, and save_protect_devices with no key to authenticate with.
Silently collecting nothing is the failure mode hardest to spot in a log.

pkg/inputunifi had no tests before this. input_test.go follows inputunas'
input_test.go: an httptest fake UNVR serving the console's SPA HTML for
everything but the Protect paths and the login, covering initialisation,
metrics, events, the filtered scrape, RawMetrics, both warnings, config binding
across toml/json/yaml/env, and that the shipped examples leave Network enabled.
TestProtectOnlyControllerFailsWithoutFlag pins the original bug against that
same console, so the flag is demonstrably what makes the difference.

Requires github.com/unpoller/unifi/v6 with NewProtectClient (unpoller/unifi#240).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GVreutpEATmBjm6PBw9RjQ
2026-08-31 13:08:27 +00:00
Cody Lee c0fbdd85c6 Merge pull request #1071 from Prototype0645/feat/wan-site-labels v5.1.1 2026-08-31 05:14:41 -05:00
Prototype0645 24329e278e feat(wan): label WAN metrics with their site and source
Every unpoller_wan_* series shipped with site_name="" and source="". The
exporter said so itself:

    cfg.WANLoadBalanceType,
    "", // site_name - will be set by caller if available
    "", // source - will be set by caller if available

The caller had nothing to set them from: WANEnrichedConfiguration carried
no identity. unifi/v6.0.3 fixes that upstream — GetWANEnrichedConfiguration
now stamps SiteName and SourceName from the site it fetched, the same way
GetSiteDPI does.

This bumps to v6.0.3 and fills the labels in. Two slices needed it, not
one: the base label set and the provider label set built further down for
the isp_name/isp_city descriptors. The test caught the second, which I had
missed.

Why it matters: an instance polling several controllers emitted WAN
metrics that were indistinguishable from one another, since wan_id is the
only other distinguishing label. Attributing them downstream meant
hardcoding a mapping in the scrape config and hoping no second controller
ever gained a gateway — when one does, its metrics are silently filed
under the wrong customer. No error, no missing series, just wrong data.

Tests use the fakeReport already present in the package. They assert every
emitted metric carries both labels, and that a nil configuration still
produces nothing rather than panicking a poll.
TestExportWANIsAttributed fails on master and passes with this change.
2026-08-31 12:06:58 +02:00
Cody Lee 76fbddf810 Merge pull request #1069 from unpoller/dependabot/go_modules/all-c0562bfa1b 2026-08-31 04:55:34 -05:00
dependabot[bot] 168930827b build(deps): bump the all group across 1 directory with 6 updates
Bumps the all group with 3 updates in the / directory: [go.opentelemetry.io/otel](https://github.com/open-telemetry/opentelemetry-go), [go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc](https://github.com/open-telemetry/opentelemetry-go) and [go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp](https://github.com/open-telemetry/opentelemetry-go).


Updates `go.opentelemetry.io/otel` from 1.45.0 to 1.46.0
- [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases)
- [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md)
- [Commits](https://github.com/open-telemetry/opentelemetry-go/compare/v1.45.0...v1.46.0)

Updates `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc` from 1.45.0 to 1.46.0
- [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases)
- [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md)
- [Commits](https://github.com/open-telemetry/opentelemetry-go/compare/v1.45.0...v1.46.0)

Updates `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp` from 1.45.0 to 1.46.0
- [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases)
- [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md)
- [Commits](https://github.com/open-telemetry/opentelemetry-go/compare/v1.45.0...v1.46.0)

Updates `go.opentelemetry.io/otel/metric` from 1.45.0 to 1.46.0
- [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases)
- [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md)
- [Commits](https://github.com/open-telemetry/opentelemetry-go/compare/v1.45.0...v1.46.0)

Updates `go.opentelemetry.io/otel/sdk` from 1.45.0 to 1.46.0
- [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases)
- [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md)
- [Commits](https://github.com/open-telemetry/opentelemetry-go/compare/v1.45.0...v1.46.0)

Updates `go.opentelemetry.io/otel/sdk/metric` from 1.45.0 to 1.46.0
- [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases)
- [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md)
- [Commits](https://github.com/open-telemetry/opentelemetry-go/compare/v1.45.0...v1.46.0)

---
updated-dependencies:
- dependency-name: go.opentelemetry.io/otel
  dependency-version: 1.46.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: all
- dependency-name: go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc
  dependency-version: 1.46.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: all
- dependency-name: go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp
  dependency-version: 1.46.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: all
- dependency-name: go.opentelemetry.io/otel/metric
  dependency-version: 1.46.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: all
- dependency-name: go.opentelemetry.io/otel/sdk
  dependency-version: 1.46.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: all
- dependency-name: go.opentelemetry.io/otel/sdk/metric
  dependency-version: 1.46.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: all
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-31 09:42:09 +00:00
Cody Lee 611cdd01fb Merge pull request #1070 from Prototype0645/chore/bump-unifi-v6.0.2 2026-08-31 04:39:59 -05:00
Prototype0645 b479d3db91 chore(deps): bump github.com/unpoller/unifi/v6 to v6.0.2 2026-08-31 08:18:56 +02:00
David Newhall II 009c3a898b Merge pull request #1067 from unpoller/feat/code-signing
ci: Authenticode-sign Windows and notarize macOS universal binary
2026-08-30 14:23:44 -07:00
David Newhall IIandCursor eeda665b7e ci: Authenticode-sign Windows and notarize the macOS universal binary.
Release tags now fail closed without signing secrets, ship a signed Windows PE via house signerd, and replace per-arch Darwin tarballs with one stapled universal archive.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-30 13:06:00 -07:00
Cody Lee 3a595a6896 Merge pull request #1065 from unpoller/fix/remote-default-site-name-override-1057
fix: respect configured default_site_name_override in remote mode
v5.1.0
2026-08-27 16:59:25 -05:00
Cody LeeandCursor ce4cfe3971 fix: respect configured default_site_name_override in remote mode
Remote API discovery was unconditionally overwriting default_site_name_override
with the console name for Cloud Gateways. Only apply the console name fallback
when the user has not already configured an override.

Fixes #1057

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-27 16:57:41 -05:00
Cody Lee 71ac64499c Merge pull request #1064 from unpoller/feat/influxdb-v3-support
Add InfluxDB v3 support and fix tag/field schema collisions
2026-08-27 16:55:55 -05:00
Cody LeeandCursor 9bc7f2c5bf Complete InfluxDB v3 rollout: tests, docs, and docker example.
Add v3 integration and version tests, migration notes, InfluxDB 3 docker-compose stack, and README updates to finish the remaining plan phases.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-27 16:24:09 -05:00
Cody LeeandCursor 5e31d2f252 Add InfluxDB v3 support and fix tag/field schema collisions.
Introduce explicit version selection with the influxdb3-go client alongside existing v1 and v2 paths, and resolve overlapping tag/field keys required for InfluxDB 3 write validation.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-27 15:49:59 -05:00
Cody Lee 3df98dcc45 Merge pull request #1063 from unpoller/fix/release-makefile-go-install
fix: use go install for release build tool dependencies
v5.0.1
2026-08-27 13:00:54 -05:00
Cody LeeandCursor de8a6e46f1 fix: use go install for release build tool dependencies
GoReleaser's make man hook failed because go get no longer works
outside a module. Replace deprecated go get with go install for
md2roff and rsrc.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-27 12:58:36 -05:00
Cody Lee 6e468667cd Merge pull request #1056 from unpoller/feat/protect-device-metrics
Add UniFi Protect device metrics
v5.0.0
2026-08-27 12:50:56 -05:00
Cody Lee 98e95050e2 Merge branch 'master' into feat/protect-device-metrics 2026-08-27 12:12:17 -05:00
Cody Lee 9472f349d1 Merge pull request #1062 from unpoller/fix/golangci-lint-go127
ci: bump golangci-lint to v2.13 for Go 1.27 deps
2026-08-27 12:11:59 -05:00
Cody LeeandCursor b753232e6a ci: bump golangci-lint to v2.13 for Go 1.27 deps
golangci-lint v2.9 is built with Go 1.26 and panics when type-checking
dependencies that include go1.27-only source files.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-27 12:09:13 -05:00
Cody Lee cb639e0c5c Merge branch 'master' into feat/protect-device-metrics 2026-08-27 09:58:11 -05:00
Cody Lee 2beb8839b9 Merge pull request #1060 from unpoller/dependabot/go_modules/all-b0668f810d 2026-08-24 18:30:47 -05:00
dependabot[bot] 9a8ad81abb build(deps): bump the all group with 2 updates
Bumps the all group with 2 updates: [github.com/DataDog/datadog-go/v5](https://github.com/DataDog/datadog-go) and [github.com/stretchr/testify](https://github.com/stretchr/testify).


Updates `github.com/DataDog/datadog-go/v5` from 5.9.0 to 5.9.1
- [Release notes](https://github.com/DataDog/datadog-go/releases)
- [Changelog](https://github.com/DataDog/datadog-go/blob/master/CHANGELOG.md)
- [Commits](https://github.com/DataDog/datadog-go/compare/v5.9.0...v5.9.1)

Updates `github.com/stretchr/testify` from 1.12.0 to 1.12.1
- [Release notes](https://github.com/stretchr/testify/releases)
- [Commits](https://github.com/stretchr/testify/compare/v1.12.0...v1.12.1)

---
updated-dependencies:
- dependency-name: github.com/DataDog/datadog-go/v5
  dependency-version: 5.9.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: all
- dependency-name: github.com/stretchr/testify
  dependency-version: 1.12.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: all
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-24 02:42:40 +00:00
Cody Lee 192ee254f9 Merge pull request #1059 from NotAFlightRisk/fix-switch-stat-rx-packets v4.0.1 2026-08-22 22:01:42 -05:00
Iain 12ac4ed7f7 fix: influxdb and datadog reported tx packets under stat_rx_packets 2026-08-23 00:19:29 +01:00
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
Cody Lee 204b51bca4 Merge pull request #1055 from unpoller/feat/unas-support
feat: opt-in UNAS Pro support (#785) + fix AppendMetrics dropping SpeedTests
v4.0.0
2026-08-19 10:38:33 -05:00
Cody LeeandClaude Opus 5 01ac7ca2b7 refactor(unas): replace disable flag with enable, defaulting to off
`disable = false` is a double negative, and a bool named disable cannot
express opt-in anyway: it zero-values to false, so the flag was inert and
opt-in rested entirely on the device list being empty.

`enable` defaults to false and is now the real gate -- Initialize, Metrics
and DebugInput all return early unless it is set. The two existing guards
remain: an empty device list is still a no-op, and no default URL is ever
synthesized.

Configuring devices while enable is false is always a mistake, so that
combination logs one error instead of silently collecting nothing.

Adds binding tests for the flag across toml, json, yaml and UP_UNAS_ENABLE
(the env name derives from the xml tag, not the json one), plus a test that
all three shipped examples default to off. Both were verified by mutation:
breaking a struct tag or flipping an example fails the suite.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-19 09:34:02 -05:00
Cody LeeandClaude Opus 5 3d178bed8d fix: AppendMetrics silently dropped SpeedTests and the batch timestamp
AppendMetrics merges every slice field on Metrics except SpeedTests, so
speed test results were discarded on the way from the input to the
outputs. Both call sites start from a non-nil &Metrics{}, so this hit
every user on every poll: inputunifi collected the results, and the
export code in promunifi, influxunifi and datadogunifi was dead.

TS was dropped the same way. The aggregate starts bare and nothing
restored the timestamp, so it stayed zero -- and influxunifi's collect()
stamps any point that carries no timestamp of its own with the
aggregate's, which meant the zero time. Both Influx clients omit a zero
timestamp and let the server assign one, so the damage was limited to
points being stamped on arrival rather than at poll time, but it made
the fallback path meaningless. First writer wins: the earliest input's
timestamp is the one that describes the batch.

The failure mode here is what makes it worth guarding rather than just
patching. A new metric family needs a field on Metrics and an append
line in AppendMetrics, and omitting the second loses every metric in
that family with no error, no log line, and a passing build.
TestAppendMetricsCoversEverySliceField walks the struct by reflection
and fails naming any slice field that is not merged, so a new field is
covered the moment it is declared rather than when someone notices the
graphs are empty.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-19 07:59:04 -05:00
Cody LeeandClaude Opus 5 d5dfc771d5 feat: add opt-in UNAS Pro support (closes #785)
Adds a new `unas` input plugin that polls UNAS Pro storage consoles and
exports console health, storage pools, disks and shares to Prometheus,
InfluxDB and DataDog.

UNAS is a separate plugin rather than a device type inside inputunifi
because a storage-only console has no Network application: it cannot
answer /status, has no sites, and shares none of the UniFi device schema.

The plugin is opt-in and inert until an operator names a console. Opt-in
is expressed as "no devices configured" rather than a `disable` flag,
because a bool named `disable` zero-values to false and so cannot make a
plugin default-off. Initialize returns silently on an empty device list
and, unlike inputunifi, nothing synthesizes a default URL.

Two behaviours are worth calling out for reviewers:

  - Metrics returns (metrics, nil) whenever any console was collected.
    poller.collectMetrics uses `if err != nil {} else if metric != nil`,
    so returning both would discard every healthy console because one
    failed. Only a total failure returns an error.

  - Re-auth fires on total failure, not on a 401. A mid-session 401 from
    GetData surfaces as ErrInvalidStatusCode, not ErrAuthenticationFailed,
    so there is no sentinel to match on. Session expiry fails all four
    endpoints at once, which is exactly the total-failure case.

Prometheus metrics use the `unifi_unas_` prefix, which diverges from the
`unas_` prefix used by the reference implementation; dashboards built
against that will need query edits.

Requires unifi/v5 v5.31.0 for the UNAS client and structs.

Credit to alexgreenbank/unaspoller for mapping the endpoints.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-19 07:52:37 -05:00