# `Improv.GattServer`
[🔗](https://github.com/bbangert/improv/blob/v0.1.1/lib/improv/gatt_server.ex#L1)

Exports the Improv GATT application over D-Bus and serves the inbound method
calls BlueZ makes against it.

Same export+dispatch shape as `Bluez.Agent`/`Client`, scaled to
a small object tree: an app-root `ObjectManager`, one `org.bluez.GattService1`,
and five `org.bluez.GattCharacteristic1` objects (the Improv
capabilities/current-state/error-state/rpc-command/rpc-result characteristics —
all cleartext, **no encrypt flags**). Registration calls
`GattManager1.RegisterApplication` on the adapter; BlueZ then reads the entire
tree once via `GetManagedObjects` and afterwards calls
`ReadValue`/`WriteValue`/`StartNotify`/`StopNotify` per characteristic.

Notifications (current-state, error-state, rpc-result progress) are pushed by
emitting `org.freedesktop.DBus.Properties.PropertiesChanged` on the
characteristic's object path (`Bluez.Rebus.emit_signal/2`) — but
only after a client has subscribed via `StartNotify`.

Owns its own `rebus` connection (separate failure domain, like `Gatt`).
Inbound rpc-command writes and notify subscriptions are forwarded to the
Improv manager process (`:manager` option) which drives the state machine.

The tree/props/read logic is pure (`managed_objects/2`, `props_for/3`,
`read_value/2`, `introspect_xml/1`, `permission` helpers) and host-tested;
the GenServer is a thin I/O shell over it.

# `app_root`

```elixir
@spec app_root() :: String.t()
```

Object path the Improv GATT application is rooted at.

# `char_path`

```elixir
@spec char_path(atom()) :: String.t() | nil
```

Object path for a characteristic role, or `nil` if unknown.

# `chars`

```elixir
@spec chars() :: [
  %{key: atom(), path: String.t(), uuid: String.t(), flags: [String.t()]}
]
```

The exported characteristics in tree order, each a map with `:key`, `:path`,
`:uuid`, and `:flags`. Pure.

# `child_spec`

Returns a specification to start this module under a supervisor.

See `Supervisor`.

# `introspect_xml`

```elixir
@spec introspect_xml(String.t()) :: String.t()
```

Introspection XML for one exported object path. Pure.

# `managed_objects`

```elixir
@spec managed_objects(%{required(atom()) =&gt; binary()}, MapSet.t()) :: list()
```

Build the `GetManagedObjects` reply tree (`a{oa{sa{sv}}}` payload) from the
current `values` cache and `notifying` set. Pure.

# `notify`

```elixir
@spec notify(GenServer.server(), atom(), binary()) :: :ok
```

Cache a new value for characteristic `key` and, if a client has subscribed,
push it as a GATT notification. Used for current-state, error-state and
rpc-result updates.

# `props_for`

```elixir
@spec props_for(String.t(), %{required(atom()) =&gt; binary()}, MapSet.t()) ::
  {String.t(), list()} | :unknown
```

Properties for the object at `path`: `{interface, props_list}` or `:unknown`.
Pure; used by `Properties.GetAll`/`Get` and the tree builder.

# `read_value`

```elixir
@spec read_value(String.t(), %{required(atom()) =&gt; binary()}) ::
  {:ok, [byte()]} | {:error, String.t()}
```

Read a characteristic value as a list of bytes (the `ay` body shape), or
`{:error, bluez_error_name}` for a non-readable / unknown characteristic. Pure.

# `register`

```elixir
@spec register(GenServer.server()) :: :ok
```

Register the GATT application with BlueZ (idempotent; runs off-loop).

# `start_link`

# `unregister`

```elixir
@spec unregister(GenServer.server()) :: :ok
```

Unregister the GATT application (idempotent).

---

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