# `ExOxvif`
[🔗](https://github.com/cortfritz/ex_oxvif/blob/v0.1.0/lib/ex_oxvif.ex#L1)

ONVIF IP camera control for Elixir, backed by the Rust
[oxvif](https://github.com/smiti1642/oxvif) client.

Find cameras with `discover/1`, open a session with `connect/2`, then use
the service modules:

  * `ExOxvif.Device` - identity, clock, services
  * `ExOxvif.Media` - profiles, RTSP stream and snapshot URIs
  * `ExOxvif.PTZ` - pan, tilt, zoom, presets, home
  * `ExOxvif.Imaging` - focus
  * `ExOxvif.Events` - pull-point event subscriptions

For example:

```elixir
{:ok, [camera | _]} = ExOxvif.discover()
{:ok, session} = ExOxvif.connect(hd(camera.xaddrs), username: "admin", password: "secret")
{:ok, [profile | _]} = ExOxvif.Media.profiles(session)
:ok = ExOxvif.PTZ.absolute_move(session, profile.token, pan: 0.25, tilt: -0.1, zoom: 0.5)
```

## How calls run

Each call is a NIF that returns at once. The request itself runs on a
small Tokio runtime inside the native library, which sends the result
back to the calling process. Camera network I/O never occupies a BEAM
scheduler, and a slow camera only delays the process that asked.

Every call accepts a `:timeout` option in milliseconds (default 15000).
oxvif bounds each HTTP exchange at 10 seconds, so the timeout is a
backstop. If it fires, the call returns `{:error, %ExOxvif.Error{kind:
:timeout}}`, and a reply that arrives later may land in the caller's
mailbox as `{:ex_oxvif_reply, id, result}`. Processes that make calls
with short timeouts (a GenServer, say) should ignore that message in
`handle_info/2`.

All results are `{:ok, value}`, `:ok`, or `{:error, %ExOxvif.Error{}}`.

# `call_opts`

```elixir
@type call_opts() :: [{:timeout, timeout()}]
```

# `connect`

```elixir
@spec connect(String.t(), keyword()) ::
  {:ok, ExOxvif.Session.t()} | {:error, ExOxvif.Error.t()}
```

Connect to a device's ONVIF device service URL, usually
`http://<host>/onvif/device_service`.

Fetches the device's capabilities and caches its service URLs.

Options:

  * `:username`, `:password` - credentials for WS-Security `UsernameToken`
    and HTTP Digest. Give both or neither.
  * `:clock_sync` - read the device clock first and offset WS-Security
    timestamps to match it (default `true`). Cameras with a drifted clock
    otherwise reject authenticated requests.
  * `:timeout` - call timeout in milliseconds

# `discover`

```elixir
@spec discover(keyword()) ::
  {:ok, [ExOxvif.DiscoveredDevice.t()]} | {:error, ExOxvif.Error.t()}
```

Probe the local network for ONVIF devices with WS-Discovery multicast.

Options:

  * `:probe_timeout` - how long to listen for answers, in milliseconds
    (default 3000)
  * `:timeout` - overall call timeout (default `probe_timeout + 5000`)

---

*Consult [api-reference.md](api-reference.md) for complete listing*
