Commit Graph
38 Commits
Author SHA1 Message Date
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 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 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 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
Cody LeeandClaude Opus 4.7 dabfeffe66 fix(prometheus): serve scrapes from cached background poll (#1013)
Decouples Prometheus scrape cadence from upstream UniFi API calls so a
429 backoff loop on the controller side no longer stalls /metrics. The
output plugin now owns a 60s background poller (configurable) whose
result is served from an in-memory cache. Concurrent /scrape requests
for the same target are coalesced via singleflight to prevent a noisy
scraper from multiplying upstream load.

Adds two new metrics so operators can detect cache staleness and
refresh failures independently:
- unpoller_prometheus_cache_age_seconds
- unpoller_prometheus_refresh_failures_total

Background goroutine recovers from panics so a malformed input payload
no longer silently kills refreshes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 17:43:44 -05:00
Cody LeeandClaude Opus 4.7 511c524e6e feat(influxunifi): add global tags applied to every measurement
Closes #1001. Mirrors the DataDog plugin's global tags feature for
InfluxDB. Per-metric tags take precedence on key collision so
site/device identifiers can never be overwritten by a misconfigured
global. Configurable via TOML/JSON/YAML under influxdb.tags.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 08:59:16 -05:00
Cody LeeandClaude Sonnet 4.5 07781214c3 Add config option to suppress unknown device type messages
Adds log_unknown_types config option (default: false) to control logging
of unknown UniFi device types. When disabled (default), unknown devices
are silently ignored to reduce log volume. When enabled, they are logged
as DEBUG messages instead of ERROR. Addresses issue #912.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-25 11:24:33 -06:00
Sofiane A 5a89a4634a Add default_site_name_override to support customizable default site names 2025-04-29 16:12:32 +02:00
Cody Lee c002283387 fixes the webserver default file path issues 2023-04-13 09:56:24 -05:00
Cody Lee 8a3bb1a562 add documentation for timeout 2022-12-08 21:14:43 -06:00
Cody Lee bd51cf59f3 move to a common pattern on output plugins to catch common issues 2022-12-05 22:00:48 -06:00
davidnewhall2 6197177a45 update config file options 2020-07-19 00:17:47 -07:00
davidnewhall2 eacce79785 updates 2020-06-29 22:36:05 -07:00
davidnewhall2 101b066036 update examples 2020-06-25 22:39:42 -07:00
davidnewhall2 4a4160d10c Add events 2020-06-20 04:18:41 -07:00
davidnewhall2 b32a9edebf fix 2020-06-15 06:05:37 -07:00
davidnewhall2 9f58c65acc update docs and examples 2020-06-15 02:19:09 -07:00
davidnewhall2 2e252b5069 Fix defaults 2020-02-03 00:10:00 -08:00
davidnewhall2 bd0680b92a Add DPI collection 2019-12-27 23:59:16 -08:00
davidnewhall2 3f6a3e9721 ocd 2019-12-20 03:22:41 -08:00
davidnewhall2 ed3bed4c7b rename some things 2019-12-20 03:19:00 -08:00
davidnewhall2 de16ba2399 fixes 2019-12-20 02:44:53 -08:00
davidnewhall2 44c544d8e1 Add dynamic plugin support 2019-12-16 03:11:40 -08:00
davidnewhall2 ac39d1727f make output plugins call in to initialize 2019-12-15 02:52:43 -08:00
davidnewhall2 510251252b update docs 2019-12-14 19:37:41 -08:00
davidnewhall2 cef7815e2d allow more data reduction 2019-11-30 03:42:17 -08:00
davidnewhall2 640075da94 fix more port numbers 2019-11-25 00:19:05 -08:00
davidnewhall2 7094c2ce2c this is just a start, nothing works yet 2019-11-12 00:04:01 -08:00
David Newhall II bdfec45579 Remove max errors parameter 2019-09-25 00:12:09 -07:00
davidnewhall2 de365d802f Fix some bugs. 2019-09-20 18:36:20 -07:00
David Newhall II b0fbfff621 Fix default user in config files. 2019-08-26 02:16:44 -07:00
David Newhall II 10d17b4a31 Add reauth feature, remove errors library. 2019-08-25 01:54:19 -07:00
David Newhall II 148b5776cd Add IDS/IPS support. 2019-07-11 01:18:40 -07:00
David Newhall II df46550a4f Call it mode. 2019-06-23 19:55:26 -07:00
David Newhall II fec1252d5e update examples for lambda config 2019-06-23 16:28:51 -07:00
David Newhall II 04e6d67489 Man file and config file updates. 2019-06-22 19:41:44 -07:00
David Newhall II d0b462523b fixes 2019-06-21 04:29:07 -07:00
David Newhall II 83c5a862b9 More untested thingies. 2019-06-21 04:09:12 -07:00