Improv.GattServer (improv v0.1.0)

Copy Markdown View Source

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.

Summary

Functions

Object path the Improv GATT application is rooted at.

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

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

Returns a specification to start this module under a supervisor.

Introspection XML for one exported object path. Pure.

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

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.

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

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 the GATT application with BlueZ (idempotent; runs off-loop).

Unregister the GATT application (idempotent).

Functions

app_root()

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

Object path the Improv GATT application is rooted at.

char_path(key)

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

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

chars()

@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(init_arg)

Returns a specification to start this module under a supervisor.

See Supervisor.

introspect_xml(path)

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

Introspection XML for one exported object path. Pure.

managed_objects(values, notifying)

@spec managed_objects(%{required(atom()) => binary()}, MapSet.t()) :: list()

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

notify(server \\ __MODULE__, key, bytes)

@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(path, values, notifying)

@spec props_for(String.t(), %{required(atom()) => 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(path, values)

@spec read_value(String.t(), %{required(atom()) => 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(server \\ __MODULE__)

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

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

start_link(opts \\ [])

unregister(server \\ __MODULE__)

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

Unregister the GATT application (idempotent).