UniFi Go Library Development
This skill provides expert guidance for developing with **github.com/unpoller/unifi/v5**, a Go library that connects to Ubiquiti UniFi controllers to pull data (clients, devices, sites, alarms, events, etc.). This library is read-only by design.
What This Skill Does
Guides implementation of UniFi controller API integrationsHelps structure data fetching patterns (sites → devices/clients)Ensures adherence to project linting and coding standardsAssists with testing patterns using mocks and testifyProvides context on UniFi API endpoints and response formatsSupports both local controller and remote Site Manager (cloud) APIsCore Library Patterns
Basic Usage Flow
1. Create `*Config` with controller URL, credentials (user/pass or API key)
2. Call `NewUnifi(config)` to get authenticated `*Unifi` client
3. Fetch `GetSites()` to retrieve site list
4. For each site, call `GetClients(sites)`, `GetDevices(sites)`, etc.
5. Optionally fetch `GetAlarms`, `GetEvents`, `GetAnomalies`, etc.
Data Structures
`GetSites()` returns `[]*Site``GetClients(sites)` returns `[]*Client``GetDevices(sites)` returns `*Devices` with typed slices: `UAPs`, `USWs`, `USGs`, `UDMs`, `UXGs`, `PDUs`, `UBBs`, `UCIs`Single-site helpers exist: `GetUAPs(site)`, `GetUSWs(site)`, etc.Go Coding Standards
Linting Requirements
**Linter config**: `.golangci.yaml` with `nlreturn`, `revive`, `tagalign`, `testpackage`, `wsl_v5`**Max issues**: 0 (must pass cleanly)**Fix mode**: Enabled (`fix: true`)**wsl_v5**: Use blank lines between logical blocks; keep control structures short**Run before commit**: `golangci-lint run`Code Style
**Imports**: Group stdlib first, then third-party**Errors**: Wrap with `fmt.Errorf("context: %w", err)`; use package sentinels (`ErrAuthenticationFailed`)**Tests**: Use `package unifi` with `// nolint: testpackage` when needed**Test tools**: `t.Parallel()`, `github.com/stretchr/testify/assert`, `github.com/stretchr/testify/require`**Mocking**: Implement `UnifiClient` interface; see `mocks.MockUnifi`**Verify interfaces**: `var _ unifi.UnifiClient = &YourClient{}`Testing
Run `go test ./...` before committingPrefer parallel tests with `t.Parallel()`Mock HTTP calls via `UnifiClient` interfaceUse `assert`/`require` from testify for assertionsUniFi API Integration
Local Controller API
**Authentication**:
Cookie-based: `/api/login` (standard) or `/api/auth/login` (UDM Pro)Alternative: API key via `X-API-KEY` headerLibrary handles both via `Config.User`/`Config.Pass` or `Config.APIKey`**Endpoint pattern**: `/api/s/{site}/...` where `{site}` is typically `"default"`
UDM Pro requires `/proxy/network` prefix**Response format**:
```json
{"data": [...], "meta": {"rc": "ok", "count": 123}}
```
**Error format**:
```json
{"data": [], "meta": {"msg": "api.err.LoginRequired", "rc": "error"}}
```
**Key endpoints** (constants in `types.go`):
`/api/s/{site}/stat/device` - Devices`/api/s/{site}/stat/sta` - Active clients`/api/s/{site}/stat/event` - Events`/api/s/{site}/list/alarm` - Alarms`/api/s/{site}/stat/stadpi` - Client DPI stats`/api/s/{site}/stat/sitedpi` - Site DPI stats`/api/s/{site}/stat/anomalies` - Anomalies**Code patterns**:
Use `u.GetData(apiPath, &response)` for GET requestsBuild paths: `fmt.Sprintf(APIDevicePath, site.Name)`Most fetches are per-site (loop over sites)Device parsing uses `json.RawMessage` and type detection in `parseDevices`Remote Site Manager API (Cloud)
**Base URL**: `https://api.ui.com/v1/`
**Authentication**: API key in `X-API-Key` header (read-only)
**Rate limits**:
Early Access: 100 requests/minutev1 stable: 10,000 requests/minute**Implementation**: See `remote.go` (`RemoteAPIClient`)
Adding New Features
General Pattern
1. Follow existing patterns in `GetDevices` / `GetClients`
2. Use `GetData(apiPath, &response)` for HTTP requests
3. Add corresponding tests following existing patterns
4. Update mocks to implement full `UnifiClient` interface
5. Run linter and tests before committing
Adding Device/Client Types
1. Extend `Devices` struct with new slice field
2. Update `parseDevices` to handle new type detection
3. Add getter methods following `GetUAPs`/`GetUSWs` style
4. Update `mocks.MockUnifi` to return mock data
5. Add tests for new type parsing and retrieval
Adding API Endpoints
1. Define constant in `types.go` (e.g., `APINewFeaturePath`)
2. Create method on `*Unifi` using `GetData`
3. Define response struct matching API format
4. Add interface method to `UnifiClient`
5. Implement in `mocks.MockUnifi`
6. Add unit tests with mock data
Architecture Notes
**Package layout**: Single package `unifi` for core logic**CLI**: Separate `main/` package**Mocks**: `mocks/` implements `UnifiClient` for testing**UnifiClient interface**: Defined in `types.go`; enables dependency injection**Config**: Contains `URL`, `User`, `Pass`, optional `APIKey`, loggers, SSL settings**Loggers**: `ErrorLog` / `DebugLog` accept `(format, args...)` for custom loggingImportant Constraints
This library is **read-only** by design (no controller updates)All public APIs must implement `UnifiClient` interfaceZero linter issues required before mergeTests must pass with `go test ./...`Handle `meta.count` for truncated API resultsSupport both standard controllers and UDM Pro endpointsExample Usage
```go
config := &unifi.Config{
URL: "https://unifi.local:8443",
User: "admin",
Pass: "password",
VerifySSL: false,
}
client, err := unifi.NewUnifi(config)
if err != nil {
return fmt.Errorf("failed to create client: %w", err)
}
sites, err := client.GetSites()
if err != nil {
return fmt.Errorf("failed to get sites: %w", err)
}
devices, err := client.GetDevices(sites)
if err != nil {
return fmt.Errorf("failed to get devices: %w", err)
}
// Access typed device slices
for _, uap := range devices.UAPs {
fmt.Printf("AP: %s (%s)\n", uap.Name, uap.MAC)
}
```
Resources
**Types**: See `types.go` for all structs, constants, and interfaces**API paths**: Defined as constants (e.g., `APIDevicePath`, `APIClientPath`)**Testing**: Reference existing `*_test.go` files for patterns**Mocking**: See `mocks/mocks.go` for `MockUnifi` implementation