An Elixir implementation of the D-Bus message protocol.
Bluez.Rebus provides a clean, Elixir-native interface for communicating over D-Bus, the inter-process communication (IPC) and remote procedure call (RPC) mechanism that is standard on Linux desktop systems.
Overview
D-Bus is a message bus system that allows multiple processes to communicate with each other in a structured way. Bluez.Rebus implements the D-Bus wire protocol and provides an easy-to-use API for:
- Connecting to D-Bus message buses (system and session buses)
- Sending method calls and receiving replies
- Emitting and receiving signals
- Publishing and consuming D-Bus services
Quick Start
# Connect to the session bus
{:ok, conn} = Bluez.Rebus.connect(:session)
# Add a signal handler to receive all signals
ref = Bluez.Rebus.add_signal_handler(conn)
# Later, remove the signal handler
Bluez.Rebus.remove_signal_handler(conn, ref)Connection Types
Bluez.Rebus supports connecting to different types of D-Bus endpoints:
:system- Connects to the system bus using the address specified in application config (see below) or the/run/dbus/system_bus_socketby default.:session- Connects to the session bus using the address specified in theDBUS_SESSION_BUS_ADDRESSenvironment variable.%{family: :local, path: path}- Unix domain socket connection to a local D-Bus daemon%{family: :inet, addr: {ip, port}}- TCP/IP connection to a remote D-Bus daemon
Configuration
You can configure the system bus address in your application's config:
config :rebus, :system_bus_address, "unix:path=/run/dbus/system_bus_socket"Architecture
When you connect to a D-Bus bus using connect/2, Bluez.Rebus creates a supervised
connection process that handles the low-level protocol details. The connection
manages authentication, message serialization/deserialization, and maintains
the persistent connection to the bus.
Error Handling
All functions return standard Elixir {:ok, result} or {:error, reason} tuples.
Connection failures, authentication errors, and protocol violations are properly
propagated as error tuples.
Examples
# Connect to session bus with default options
{:ok, conn} = Bluez.Rebus.connect(:session)
# Connect to a Unix domain socket
{:ok, conn} = Bluez.Rebus.connect(%{family: :local, path: "/tmp/dbus-socket"})For more advanced usage, see the documentation for Bluez.Rebus.Message and other
modules in this package.
Summary
Functions
Adds a signal handler to receive D-Bus signals on the connection.
Establishes a connection to a D-Bus message bus.
Same as connect/2, but raises an exception on failure.
Removes a previously registered signal handler from the connection.
Emit a D-Bus :signal on conn.
Reply to an inbound method call request with a :method_return. body is
the reply arguments (default none); pass signature when the body is
non-empty (e.g. "a{sv}").
Reply to an inbound method call request with a D-Bus error
(e.g. "org.freedesktop.DBus.Error.UnknownMethod").
Register handler to receive inbound method calls on conn as
{:dbus_call, %Bluez.Rebus.Message{type: :method_call}} messages. The handler
replies with reply/4 or reply_error/4.
Types
@type address() :: :system | :session | :socket.sockaddr_in() | :socket.sockaddr_un()
Functions
Adds a signal handler to receive D-Bus signals on the connection.
Signal handlers receive all D-Bus signals that arrive on the connection. Multiple signal handlers can be registered on the same connection, and each will receive copies of all signals.
Parameters
conn- The connection PID returned fromconnect/2
Return Values
reference()- A unique reference that identifies this signal handler
Examples
{:ok, conn} = Bluez.Rebus.connect(%{family: :local, path: "/tmp/my-dbus"})
ref = Bluez.Rebus.add_signal_handler(conn)
# The calling process will now receive messages like:
# {^ref, %Bluez.Rebus.Message{type: :signal, ...}}Signal Message Format
When a D-Bus signal is received, registered signal handlers will receive a message in the format:
{^ref, %Bluez.Rebus.Message{
type: :signal,
header_fields: %{
path: "/path/to/object",
interface: "com.example.Interface",
member: "SignalName",
sender: "com.example.Service"
},
body: [signal_args...],
signature: "signal_signature"
}}Notes
Signal handlers should be prepared to handle a potentially high volume of messages depending on the activity on the D-Bus. Consider using selective receive or GenServer message handling for robust signal processing.
Remember to call remove_signal_handler/2 when you no longer need to
receive signals to avoid message queue buildup.
Signal handlers are automatically cleaned up when the connection is closed or when the handler exits.
@spec connect( address(), keyword() ) :: DynamicSupervisor.on_start_child()
Establishes a connection to a D-Bus message bus.
Creates a supervised connection process that handles D-Bus protocol communication. The connection automatically handles authentication and maintains the persistent connection to the specified D-Bus endpoint.
Parameters
address- The D-Bus endpoint to connect to::system- Connects to the system bus using the address specified in application config (see below) or the/run/dbus/system_bus_socketby default.:session- Connects to the session bus using the address specified in theDBUS_SESSION_BUS_ADDRESSenvironment variable.%{family: :local, path: path}- Unix domain socket connection to a local D-Bus daemon%{family: :inet, addr: {ip, port}}- TCP/IP connection to a remote D-Bus daemon
opts- Optional keyword list of connection options::timeout- Connection timeout in milliseconds (default: 5000):name- Optional name for the connection process- Additional options are passed to the underlying connection process
Return Values
{:ok, pid}- Returns the PID of the connection process{:error, reason}- Connection failed due to the specified reason
Examples
# Connect to a custom Unix socket
{:ok, conn} = Bluez.Rebus.connect(%{family: :local, path: "/tmp/my-dbus"})
# Connect to a TCP endpoint
address = %{family: :inet, addr: {127, 0, 0, 1}, port: 12345}
{:ok, conn} = Bluez.Rebus.connect(address)Notes
The returned PID is for the connection process, which is the main interface for sending and receiving D-Bus messages.
Same as connect/2, but raises an exception on failure.
Removes a previously registered signal handler from the connection.
Stops the specified signal handler from receiving future D-Bus signals.
The handler is identified by the reference returned from add_signal_handler/1.
Parameters
conn- The connection PID returned fromconnect/2ref- The reference returned fromadd_signal_handler/1
Return Values
:ok- The signal handler was successfully removed
Examples
{:ok, conn} = Bluez.Rebus.connect(%{family: :local, path: "/tmp/my-dbus"})
ref = Bluez.Rebus.add_signal_handler(conn)
# ... handle signals ...
# Remove the handler when done
:ok = Bluez.Rebus.delete_signal_handler(conn, ref)Notes
After deleting a signal handler, the calling process will no longer receive signal messages for that handler. Other signal handlers on the same connection (if any) will continue to receive signals normally.
It's safe to call this function multiple times with the same reference -
subsequent calls will simply return :ok without error.
Emit a D-Bus :signal on conn.
opts are forwarded to Bluez.Rebus.Message.new!/2 and must include :path,
:interface, and :member. Pass :body and :signature when the signal
carries arguments, and an optional :destination to direct it at one peer.
Unlike reply/4, a signal is fire-and-forget: the transport skips the
pending-reply table, so there is nothing to await — this returns :ok as
soon as the frame is written. Used to push GATT notifications, e.g. a
org.freedesktop.DBus.Properties.PropertiesChanged on an exported
characteristic object.
@spec reply(pid(), Bluez.Rebus.Message.t(), [term()], String.t() | nil) :: :ok | {:error, term()}
Reply to an inbound method call request with a :method_return. body is
the reply arguments (default none); pass signature when the body is
non-empty (e.g. "a{sv}").
@spec reply_error(pid(), Bluez.Rebus.Message.t(), String.t(), String.t()) :: :ok | {:error, term()}
Reply to an inbound method call request with a D-Bus error
(e.g. "org.freedesktop.DBus.Error.UnknownMethod").
Register handler to receive inbound method calls on conn as
{:dbus_call, %Bluez.Rebus.Message{type: :method_call}} messages. The handler
replies with reply/4 or reply_error/4.