Skip to content

nyo16/unifi_api

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

8 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

UnifiApi

Elixir HTTP client for UniFi Dream Machine APIs, covering both the Network API (v10.1.84) and the Protect API (v6.2.88). Built on Req.

Installation

Add unifi_api to your list of dependencies in mix.exs:

def deps do
  [
    {:unifi_api, "~> 0.2.0"}
  ]
end

Configuration

Application config

# config/config.exs
config :unifi_api,
  base_url: "https://192.168.1.1",
  api_key: "your-api-key",
  verify_ssl: false,
  # UDM defaults — for Cloud Key, set both to "/integration"
  network_path: "/proxy/network/integration",
  protect_path: "/proxy/protect/integration"

Environment variables

# config/runtime.exs
config :unifi_api,
  base_url: System.get_env("UNIFI_BASE_URL", "https://192.168.1.1"),
  api_key: System.get_env("UNIFI_API_KEY", ""),
  verify_ssl: System.get_env("UNIFI_VERIFY_SSL", "false") == "true",
  network_path: System.get_env("UNIFI_NETWORK_PATH", "/proxy/network/integration"),
  protect_path: System.get_env("UNIFI_PROTECT_PATH", "/proxy/protect/integration")

Path prefixes

On UDM / UDM Pro / UDM SE (UniFi OS), the API runs behind a reverse proxy:

API Default path Env var
Network /proxy/network/integration UNIFI_NETWORK_PATH
Protect /proxy/protect/integration UNIFI_PROTECT_PATH

On Cloud Key or standalone controllers, set both to "/integration":

config :unifi_api,
  network_path: "/integration",
  protect_path: "/integration"

Runtime override

Pass options directly when creating a client to override application config:

client = UnifiApi.new(
  base_url: "https://192.168.0.1",
  api_key: "my-api-key",
  verify_ssl: false
)

Quick Start

# Create a client (uses application config)
client = UnifiApi.new()

# Or with explicit options
client = UnifiApi.new(base_url: "https://192.168.1.1", api_key: "my-key")

# Check the controller version
{:ok, info} = UnifiApi.Network.Info.get_info(client)
# => {:ok, %{"applicationVersion" => "10.1.84"}}

# List all sites
{:ok, sites} = UnifiApi.Network.Sites.list(client)

# List devices on a site
{:ok, devices} = UnifiApi.Network.Devices.list(client, "site-uuid")

# List Protect cameras
{:ok, cameras} = UnifiApi.Protect.Cameras.list(client)

Network API

All Network API functions require a site_id (except Info, Resources.list_dpi_categories/2, Resources.list_dpi_applications/2, Resources.list_countries/2, and Devices.list_pending/2).

Sites

{:ok, sites} = UnifiApi.Network.Sites.list(client)
# => {:ok, [%{"id" => "abc-123", "name" => "Default", "internalReference" => "default"}]}

Devices

# List all devices on a site
{:ok, devices} = UnifiApi.Network.Devices.list(client, site_id)

# Get a specific device
{:ok, device} = UnifiApi.Network.Devices.get(client, site_id, device_id)

# Get latest device statistics
{:ok, stats} = UnifiApi.Network.Devices.get_statistics(client, site_id, device_id)

# Adopt a new device
{:ok, _} = UnifiApi.Network.Devices.adopt(client, site_id, %{mac: "aa:bb:cc:dd:ee:ff"})

# Execute a device action (restart, locate, etc.)
{:ok, _} = UnifiApi.Network.Devices.execute_action(client, site_id, device_id, %{action: "restart"})

# Execute a port action (PoE cycle, etc.)
{:ok, _} = UnifiApi.Network.Devices.execute_port_action(client, site_id, device_id, 3, %{action: "cycle"})

# Remove a device
{:ok, _} = UnifiApi.Network.Devices.remove(client, site_id, device_id)

# List pending devices (not site-scoped)
{:ok, pending} = UnifiApi.Network.Devices.list_pending(client)

Clients

# List connected clients
{:ok, clients} = UnifiApi.Network.Clients.list(client, site_id)
# Each client has: type (WIRED/WIRELESS/VPN/TELEPORT), id, name, connectedAt, ipAddress, access

Networks

# List all networks
{:ok, networks} = UnifiApi.Network.Networks.list(client, site_id)

# Get a specific network
{:ok, network} = UnifiApi.Network.Networks.get(client, site_id, network_id)

# Create a network
{:ok, network} = UnifiApi.Network.Networks.create(client, site_id, %{
  name: "Guest VLAN",
  vlanId: 100
})

# Update a network
{:ok, _} = UnifiApi.Network.Networks.update(client, site_id, network_id, %{name: "New Name"})

# Delete a network
{:ok, _} = UnifiApi.Network.Networks.delete(client, site_id, network_id)

WiFi

# List WiFi broadcasts (SSIDs)
{:ok, ssids} = UnifiApi.Network.Wifi.list(client, site_id)

Firewall

# --- Zones ---
{:ok, zones} = UnifiApi.Network.Firewall.list_zones(client, site_id)
{:ok, zone} = UnifiApi.Network.Firewall.get_zone(client, site_id, zone_id)
{:ok, zone} = UnifiApi.Network.Firewall.create_zone(client, site_id, %{name: "DMZ", networkIds: [net_id]})
{:ok, _} = UnifiApi.Network.Firewall.update_zone(client, site_id, zone_id, %{name: "DMZ-Updated"})
{:ok, _} = UnifiApi.Network.Firewall.delete_zone(client, site_id, zone_id)

# --- Policies ---
{:ok, policies} = UnifiApi.Network.Firewall.list_policies(client, site_id)
{:ok, policy} = UnifiApi.Network.Firewall.get_policy(client, site_id, policy_id)
{:ok, policy} = UnifiApi.Network.Firewall.create_policy(client, site_id, %{
  name: "Block IoT to LAN",
  enabled: true,
  action: "BLOCK",
  source: %{zoneId: iot_zone_id},
  destination: %{zoneId: lan_zone_id}
})
{:ok, _} = UnifiApi.Network.Firewall.update_policy(client, site_id, policy_id, %{enabled: false})
{:ok, _} = UnifiApi.Network.Firewall.delete_policy(client, site_id, policy_id)

Hotspot Vouchers

# List all vouchers
{:ok, vouchers} = UnifiApi.Network.Hotspot.list_vouchers(client, site_id)

# Get a specific voucher
{:ok, voucher} = UnifiApi.Network.Hotspot.get_voucher(client, site_id, voucher_id)

# Create vouchers (1-1000 at a time)
{:ok, vouchers} = UnifiApi.Network.Hotspot.create_vouchers(client, site_id, %{
  count: 10,
  name: "Event Pass",
  timeLimitMinutes: 1440,
  authorizedGuestLimit: 1,
  dataUsageLimitMBytes: 500,
  rxRateLimitKbps: 5000,
  txRateLimitKbps: 1000
})

# Delete a specific voucher
{:ok, _} = UnifiApi.Network.Hotspot.delete_voucher(client, site_id, voucher_id)

# Delete all vouchers
{:ok, _} = UnifiApi.Network.Hotspot.delete_vouchers(client, site_id)

ACL Rules

# List ACL rules
{:ok, rules} = UnifiApi.Network.ACL.list(client, site_id)

# Create an ACL rule
{:ok, rule} = UnifiApi.Network.ACL.create(client, site_id, %{
  type: "IPV4",
  name: "Block SSH",
  enabled: true,
  action: "BLOCK",
  protocolFilter: %{protocol: "TCP", dstPort: 22}
})

# Update and delete
{:ok, _} = UnifiApi.Network.ACL.update(client, site_id, rule_id, %{enabled: false})
{:ok, _} = UnifiApi.Network.ACL.delete(client, site_id, rule_id)

# Manage rule ordering
{:ok, ordering} = UnifiApi.Network.ACL.get_ordering(client, site_id)
{:ok, _} = UnifiApi.Network.ACL.update_ordering(client, site_id, %{ids: ["rule-1", "rule-2"]})

DNS Policies

# List DNS policies
{:ok, policies} = UnifiApi.Network.DNS.list(client, site_id)

# Create a DNS record
{:ok, policy} = UnifiApi.Network.DNS.create(client, site_id, %{
  type: "A_RECORD",
  name: "app.local",
  value: "192.168.1.50"
})

# Supported types: A_RECORD, AAAA_RECORD, CNAME_RECORD, MX_RECORD,
#                  TXT_RECORD, SRV_RECORD, FORWARD_DOMAIN

Traffic Matching

{:ok, lists} = UnifiApi.Network.TrafficMatching.list(client, site_id)
# Types: PORTS, IPV4_ADDRESSES, IPV6_ADDRESSES

Supporting Resources

# WAN interfaces
{:ok, wans} = UnifiApi.Network.Resources.list_wans(client, site_id)

# VPN
{:ok, tunnels} = UnifiApi.Network.Resources.list_vpn_tunnels(client, site_id)
{:ok, servers} = UnifiApi.Network.Resources.list_vpn_servers(client, site_id)

# RADIUS
{:ok, profiles} = UnifiApi.Network.Resources.list_radius_profiles(client, site_id)

# Device tags
{:ok, tags} = UnifiApi.Network.Resources.list_device_tags(client, site_id)

# DPI (not site-scoped)
{:ok, categories} = UnifiApi.Network.Resources.list_dpi_categories(client)
{:ok, apps} = UnifiApi.Network.Resources.list_dpi_applications(client)

# Countries (not site-scoped)
{:ok, countries} = UnifiApi.Network.Resources.list_countries(client)

Protect API

Protect endpoints are not site-scoped.

Cameras

# List all cameras
{:ok, cameras} = UnifiApi.Protect.Cameras.list(client)

# Get a specific camera
{:ok, camera} = UnifiApi.Protect.Cameras.get(client, camera_id)

# Update camera settings
{:ok, _} = UnifiApi.Protect.Cameras.update(client, camera_id, %{
  name: "Front Door",
  micVolume: 80,
  videoMode: "highFps",
  ledSettings: %{isEnabled: false}
})

# Take a snapshot (returns JPEG binary)
{:ok, jpeg} = UnifiApi.Protect.Cameras.snapshot(client, camera_id)
File.write!("snapshot.jpg", jpeg)

# High quality snapshot
{:ok, jpeg} = UnifiApi.Protect.Cameras.snapshot(client, camera_id, high_quality: true)

# PTZ controls
{:ok, _} = UnifiApi.Protect.Cameras.ptz_goto(client, camera_id, 1)           # Go to preset slot 1
{:ok, _} = UnifiApi.Protect.Cameras.ptz_patrol_start(client, camera_id, 0)   # Start patrol slot 0
{:ok, _} = UnifiApi.Protect.Cameras.ptz_patrol_stop(client, camera_id)       # Stop patrol

NVR

{:ok, nvr} = UnifiApi.Protect.NVR.get(client)
# => {:ok, %{"id" => "...", "name" => "UNVR", "doorbellSettings" => %{...}}}

Viewers

{:ok, viewers} = UnifiApi.Protect.Viewers.list(client)
{:ok, viewer} = UnifiApi.Protect.Viewers.get(client, viewer_id)
{:ok, _} = UnifiApi.Protect.Viewers.update(client, viewer_id, %{liveview: liveview_id})

Liveviews

{:ok, liveviews} = UnifiApi.Protect.Liveviews.list(client)
# Each has: id, name, isDefault, isGlobal, owner, layout (1-26), slots

Sensors

{:ok, sensors} = UnifiApi.Protect.Sensors.list(client)
# Each has: id, name, state, mountType, batteryStatus, stats,
#           isOpened, isMotionDetected, temperature/humidity/light/leak settings

Lights

{:ok, lights} = UnifiApi.Protect.Lights.list(client)
# Each has: id, name, state, isDark, isLightOn, lastMotion,
#           lightModeSettings, lightDeviceSettings, camera

Chimes

{:ok, chimes} = UnifiApi.Protect.Chimes.list(client)
# Each has: id, name, state, cameraIds, ringSettings

Streaming & Pagination

Every list endpoint has a stream variant that returns a lazy Stream powered by Stream.resource/3. Pages are fetched on demand — no data is pulled until you consume the stream with Enum or Stream functions.

Lazy streaming (recommended)

# Stream ALL devices across pages — fetches 200 per page automatically
UnifiApi.Network.Devices.stream(client, site_id)
|> Enum.to_list()

# Only the first page is fetched
UnifiApi.Network.Devices.stream(client, site_id)
|> Enum.take(5)

# Filter + stream — composable with the full Stream/Enum API
UnifiApi.Network.Clients.stream(client, site_id, filter: "type.eq(WIRELESS)")
|> Stream.map(& &1["name"])
|> Enum.to_list()

# Count all clients without loading them all into memory at once
UnifiApi.Network.Clients.stream(client, site_id)
|> Enum.count()

# Custom page size
UnifiApi.Network.Devices.stream(client, site_id, limit: 50)
|> Enum.to_list()

# Stream firewall policies, vouchers, ACL rules, DNS, etc.
UnifiApi.Network.Firewall.stream_policies(client, site_id)
|> Stream.filter(& &1["enabled"])
|> Enum.to_list()

UnifiApi.Network.Hotspot.stream_vouchers(client, site_id)
|> Stream.reject(& &1["expired"])
|> Enum.map(& &1["code"])

UnifiApi.Network.Resources.stream_dpi_categories(client)
|> Enum.to_list()

Stream functions raise on API errors, making them safe to compose in pipelines.

Available stream functions

Module Function
Sites stream/2
Devices stream/3, stream_pending/2
Clients stream/3
Networks stream/3
Wifi stream/3
Firewall stream_zones/3, stream_policies/3
Hotspot stream_vouchers/3
ACL stream/3
DNS stream/3
TrafficMatching stream/3
Resources stream_wans/3, stream_vpn_tunnels/3, stream_vpn_servers/3, stream_radius_profiles/3, stream_device_tags/3, stream_dpi_categories/2, stream_dpi_applications/2, stream_countries/2

Manual pagination

If you need per-page control, use list with :offset and :limit:

{:ok, page1} = UnifiApi.Network.Devices.list(client, site_id, limit: 50, offset: 0)
{:ok, page2} = UnifiApi.Network.Devices.list(client, site_id, limit: 50, offset: 50)

# Filter (UniFi filter expression syntax)
{:ok, wireless} = UnifiApi.Network.Clients.list(client, site_id,
  filter: "type.eq(WIRELESS)"
)

Filter syntax

Filters use the format property.function(args) and can be combined:

Function Example
eq name.eq(Office)
ne type.ne(WIRELESS)
gt, ge, lt, le connectedAt.gt(1700000000)
in, notIn type.in(WIRED,VPN)
like name.like(cam*)
isNull, isNotNull ipAddress.isNotNull()
isEmpty name.isEmpty()
contains, containsAny, containsAll, containsExactly tags.contains(vip)

Combine with and(), or(), not().

Data Extraction Recipes

Common patterns for pulling structured data out of your UniFi controller.

Export all clients to a list of maps

client = UnifiApi.new()

all_clients =
  UnifiApi.Network.Sites.stream(client)
  |> Enum.flat_map(fn site ->
    UnifiApi.Network.Clients.stream(client, site["id"])
    |> Enum.map(&Map.put(&1, "site", site["name"]))
  end)

# Filter only wireless clients
wireless = Enum.filter(all_clients, &(&1["type"] == "WIRELESS"))

Build a device inventory CSV

client = UnifiApi.new()

rows =
  UnifiApi.Network.Sites.stream(client)
  |> Enum.flat_map(fn site ->
    UnifiApi.Network.Devices.stream(client, site["id"])
    |> Enum.map(fn device ->
      [site["name"], device["name"], device["mac"], device["model"], device["state"]]
      |> Enum.join(",")
    end)
  end)

csv = ["site,name,mac,model,state" | rows] |> Enum.join("\n")
File.write!("devices.csv", csv)

Scrape all camera snapshots

client = UnifiApi.new()
{:ok, cameras} = UnifiApi.Protect.Cameras.list(client)

for camera <- cameras, camera["state"] == "CONNECTED" do
  case UnifiApi.Protect.Cameras.snapshot(client, camera["id"], high_quality: true) do
    {:ok, jpeg} ->
      name = camera["name"] |> String.replace(~r/[^\w]/, "_")
      File.write!("snapshots/#{name}.jpg", jpeg)

    {:error, reason} ->
      IO.puts("Failed #{camera["name"]}: #{inspect(reason)}")
  end
end

Collect network topology (sites, networks, devices)

client = UnifiApi.new()

topology =
  UnifiApi.Network.Sites.stream(client)
  |> Enum.map(fn site ->
    sid = site["id"]

    %{
      site: site["name"],
      networks:
        UnifiApi.Network.Networks.stream(client, sid)
        |> Enum.map(&Map.take(&1, ["id", "name", "vlanId", "subnet"])),
      devices:
        UnifiApi.Network.Devices.stream(client, sid)
        |> Enum.map(&Map.take(&1, ["id", "name", "mac", "model", "state"]))
    }
  end)

Monitor connected client count over time

client = UnifiApi.new()
[site | _] = UnifiApi.Network.Sites.stream(client) |> Enum.take(1)

# Poll every 60 seconds
Stream.interval(60_000)
|> Stream.map(fn _ ->
  counts =
    UnifiApi.Network.Clients.stream(client, site["id"])
    |> Enum.group_by(& &1["type"])
    |> Map.new(fn {type, list} -> {type, length(list)} end)

  {DateTime.utc_now(), counts}
end)
|> Stream.each(fn {time, counts} ->
  IO.puts("#{time} | WIRED=#{counts["WIRED"] || 0} WIRELESS=#{counts["WIRELESS"] || 0} VPN=#{counts["VPN"] || 0}")
end)
|> Stream.run()

Export firewall rules

client = UnifiApi.new()

UnifiApi.Network.Sites.stream(client)
|> Enum.map(fn site ->
  sid = site["id"]

  %{
    site: site["name"],
    zones:
      UnifiApi.Network.Firewall.stream_zones(client, sid)
      |> Enum.map(&Map.take(&1, ["id", "name", "networkIds"])),
    policies:
      UnifiApi.Network.Firewall.stream_policies(client, sid)
      |> Enum.map(&Map.take(&1, ["id", "name", "enabled", "action", "source", "destination"]))
  }
end)

Export hotspot voucher codes

client = UnifiApi.new()
[site | _] = UnifiApi.Network.Sites.stream(client) |> Enum.take(1)

active =
  UnifiApi.Network.Hotspot.stream_vouchers(client, site["id"])
  |> Stream.reject(& &1["expired"])
  |> Enum.map(&Map.take(&1, ["code", "name", "timeLimitMinutes", "expiresAt"]))

# Print as a table
for v <- active do
  IO.puts("#{v["code"]}  #{v["name"]}  #{v["timeLimitMinutes"]}min")
end

Dump all Protect device info

client = UnifiApi.new()

{:ok, cameras} = UnifiApi.Protect.Cameras.list(client)
{:ok, sensors} = UnifiApi.Protect.Sensors.list(client)
{:ok, lights} = UnifiApi.Protect.Lights.list(client)
{:ok, chimes} = UnifiApi.Protect.Chimes.list(client)
{:ok, nvr} = UnifiApi.Protect.NVR.get(client)

protect_inventory = %{
  nvr: Map.take(nvr, ["id", "name", "modelKey"]),
  cameras: Enum.map(cameras, &Map.take(&1, ["id", "name", "state", "mac", "modelKey"])),
  sensors: Enum.map(sensors, &Map.take(&1, ["id", "name", "state", "batteryStatus"])),
  lights: Enum.map(lights, &Map.take(&1, ["id", "name", "state", "isLightOn"])),
  chimes: Enum.map(chimes, &Map.take(&1, ["id", "name", "state"]))
}

Dashboard data scraper

Pull everything you need for a custom dashboard in one shot — network overview, client breakdown, device health, WiFi status, and Protect camera states.

client = UnifiApi.new()

# Get controller info
{:ok, info} = UnifiApi.Network.Info.get_info(client)

# Collect per-site data
sites_data =
  UnifiApi.Network.Sites.stream(client)
  |> Enum.map(fn site ->
    sid = site["id"]

    # Clients grouped by type
    clients = UnifiApi.Network.Clients.stream(client, sid) |> Enum.to_list()

    client_breakdown =
      clients
      |> Enum.group_by(& &1["type"])
      |> Map.new(fn {type, list} -> {type, length(list)} end)

    # Devices with health status
    devices =
      UnifiApi.Network.Devices.stream(client, sid)
      |> Enum.map(fn d ->
        %{
          name: d["name"],
          mac: d["mac"],
          model: d["model"],
          state: d["state"],
          ip: d["ip"]
        }
      end)

    connected_devices = Enum.count(devices, & &1.state == "CONNECTED")
    disconnected_devices = Enum.count(devices, & &1.state == "DISCONNECTED")

    # Networks
    networks =
      UnifiApi.Network.Networks.stream(client, sid)
      |> Enum.map(&Map.take(&1, ["id", "name", "vlanId"]))

    # WiFi SSIDs
    ssids =
      UnifiApi.Network.Wifi.stream(client, sid)
      |> Enum.map(&Map.take(&1, ["id", "name", "enabled"]))

    # WANs
    wans =
      UnifiApi.Network.Resources.stream_wans(client, sid)
      |> Enum.map(&Map.take(&1, ["id", "name", "status"]))

    %{
      site_id: sid,
      site_name: site["name"],
      clients: %{
        total: length(clients),
        wired: client_breakdown["WIRED"] || 0,
        wireless: client_breakdown["WIRELESS"] || 0,
        vpn: client_breakdown["VPN"] || 0,
        teleport: client_breakdown["TELEPORT"] || 0
      },
      devices: %{
        total: length(devices),
        connected: connected_devices,
        disconnected: disconnected_devices,
        list: devices
      },
      networks: networks,
      ssids: ssids,
      wans: wans
    }
  end)

# Protect overview
{:ok, cameras} = UnifiApi.Protect.Cameras.list(client)
{:ok, sensors} = UnifiApi.Protect.Sensors.list(client)
{:ok, lights} = UnifiApi.Protect.Lights.list(client)
{:ok, nvr} = UnifiApi.Protect.NVR.get(client)

protect_data = %{
  nvr: Map.take(nvr, ["id", "name", "modelKey"]),
  cameras: %{
    total: length(cameras),
    connected: Enum.count(cameras, & &1["state"] == "CONNECTED"),
    list:
      Enum.map(cameras, fn c ->
        %{
          id: c["id"],
          name: c["name"],
          state: c["state"],
          model: c["modelKey"]
        }
      end)
  },
  sensors: %{
    total: length(sensors),
    open_doors: Enum.count(sensors, & &1["isOpened"]),
    motion_detected: Enum.count(sensors, & &1["isMotionDetected"])
  },
  lights: %{
    total: length(lights),
    on: Enum.count(lights, & &1["isLightOn"])
  }
}

# Full dashboard payload
dashboard = %{
  controller_version: info["applicationVersion"],
  scraped_at: DateTime.utc_now(),
  sites: sites_data,
  protect: protect_data
}

# Write to JSON
File.write!("dashboard.json", JSON.encode!(dashboard))

You can run this on an interval to feed a time-series database, or serve it from a Phoenix endpoint for a live dashboard:

# Poll every 30 seconds and write fresh data
Stream.interval(30_000)
|> Stream.each(fn _ ->
  # ... same scraper logic above ...
  File.write!("dashboard.json", JSON.encode!(dashboard))
  IO.puts("[#{DateTime.utc_now()}] Dashboard updated")
end)
|> Stream.run()

Formatted Output

UnifiApi.Formatter prints API responses as colored ANSI tables in iex.

Quick shortcuts

{:ok, sites} = UnifiApi.Network.Sites.list(client)
UnifiApi.Formatter.sites(sites)

{:ok, devices} = UnifiApi.Network.Devices.list(client, site_id)
UnifiApi.Formatter.devices(devices)
# State column is color-coded: green=CONNECTED, yellow=CONNECTING, red=DISCONNECTED

{:ok, clients} = UnifiApi.Network.Clients.list(client, site_id)
UnifiApi.Formatter.clients(clients)
# Type column is color-coded: blue=WIRED, magenta=WIRELESS, cyan=VPN

{:ok, cameras} = UnifiApi.Protect.Cameras.list(protect)
UnifiApi.Formatter.cameras(cameras)

{:ok, networks} = UnifiApi.Network.Networks.list(client, site_id)
UnifiApi.Formatter.networks(networks)

Custom tables

# Pick any columns
{:ok, devices} = UnifiApi.Network.Devices.list(client, site_id)
UnifiApi.Formatter.table(devices, ["name", "mac", "model", "state", "ip"],
  title: "My Devices",
  colors: %{"state" => :state}
)

# Detail view for a single record
{:ok, nvr} = UnifiApi.Protect.NVR.get(protect)
UnifiApi.Formatter.detail(nvr, title: "NVR Info")

Error Handling

All functions return {:ok, body} on success or {:error, reason} on failure:

case UnifiApi.Network.Devices.get(client, site_id, "bad-id") do
  {:ok, device} ->
    IO.inspect(device)

  {:error, {404, body}} ->
    IO.puts("Not found: #{inspect(body)}")

  {:error, {401, _}} ->
    IO.puts("Invalid API key")

  {:error, reason} ->
    IO.puts("Connection error: #{inspect(reason)}")
end

Generating Docs

mix deps.get
mix docs
open doc/index.html

License

Apache License 2.0 — see LICENSE for details.

About

Elixir client for UniFi Dream Machine APIs — Network (v10.1.84) & Protect (v6.2.88). Sites, devices, clients, networks, firewall, cameras, sensors & more.

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages