### Start a linked connection Source: https://github.com/smartrent/tortoise311/blob/main/docs/connection_supervision.md Starts a connection linked to the current process. Recommended only for experimentation in IEx as termination of either process affects the other. ```elixir Tortoise311.Connection.start_link( client_id: HeartOfGold, server: {Tortoise311.Transport.Tcp, host: 'localhost', port: 1883}, handler: {Tortoise311.Handler.Logger, []} ) ``` -------------------------------- ### Start Tortoise311 Connection and Publish Message Source: https://github.com/smartrent/tortoise311/blob/main/docs/connecting_to_a_mqtt_broker.md Example of starting a Tortoise311 MQTT connection with a specified client ID, server details, and handler, followed by publishing a message. The client ID can be an atom or a string. ```elixir {ok, _pid} = Tortoise311.Connection.start_link( client_id: MyClient, server: {Tortoise311.Transport.Tcp, host: "localhost", port: 1883}, handler: {Tortoise311.Handler.Logger, []} ) Tortoise311.publish(MyClient, "foo/bar", "hello") ``` -------------------------------- ### Start Basic MQTT Connection Source: https://github.com/smartrent/tortoise311/blob/main/docs/connecting_to_a_mqtt_broker.md Establishes a TCP connection to an MQTT broker. Requires a client ID, server details (host and port), and a handler module for logging events. Assumes anonymous, non-SSL connections. ```elixir {ok, _pid} = Tortoise311.Connection.start_link( client_id: HelloWorld, server: {Tortoise311.Transport.Tcp, host: "localhost", port: 1883}, handler: {Tortoise311.Handler.Logger, []} ) ``` -------------------------------- ### Start a Linked Connection Source: https://context7.com/smartrent/tortoise311/llms.txt Establish a connection linked to the current process or integrate it directly into an application's supervision tree. ```elixir # Start a linked connection {:ok, pid} = Tortoise311.Connection.start_link( client_id: :my_device, server: {Tortoise311.Transport.Tcp, host: "localhost", port: 1883}, handler: {Tortoise311.Handler.Logger, []}, user_name: "mqtt_user", password: "secret", keep_alive: 30, will: %Tortoise311.Package.Publish{ topic: "devices/my_device/status", payload: "offline", qos: 1, retain: true } ) # Add connection to your own supervisor defmodule MyApp.Supervisor do use Supervisor def start_link(opts) do Supervisor.start_link(__MODULE__, opts, name: __MODULE__) end def init(_opts) do children = [ {Tortoise311.Connection, [ client_id: :production_client, server: {Tortoise311.Transport.Tcp, host: "broker.local", port: 1883}, handler: {MyApp.MQTTHandler, []} ]} ] Supervisor.init(children, strategy: :one_for_one) end end ``` -------------------------------- ### Start a Supervised MQTT Connection Source: https://github.com/smartrent/tortoise311/blob/main/README.md Initiates a supervised connection to an MQTT broker and publishes a message using the TCP transport. ```elixir # connect to the server and subscribe to foo/bar with QoS 0 Tortoise311.Supervisor.start_child( client_id: "my_client_id", handler: {Tortoise311.Handler.Logger, []}, server: {Tortoise311.Transport.Tcp, host: 'localhost', port: 1883}, subscriptions: [{"foo/bar", 0}]) # publish a message on the broker Tortoise311.publish("my_client_id", "foo/bar", "Hello from the World of Tomorrow !", qos: 0) ``` -------------------------------- ### Start a child connection on the default supervisor Source: https://github.com/smartrent/tortoise311/blob/main/docs/connection_supervision.md Uses the default dynamic supervisor registered as Tortoise311.Supervisor to start a connection. ```elixir Tortoise311.Supervisor.start_child( client_id: "heart-of-gold", handler: {Tortoise311.Handler.Logger, []}, server: {Tortoise311.Transport.Tcp, host: 'localhost', port: 1883} ) ``` -------------------------------- ### Start a child connection on a custom supervisor Source: https://github.com/smartrent/tortoise311/blob/main/docs/connection_supervision.md Attaches a connection dynamically to a specific named supervisor instance. ```elixir Tortoise311.Supervisor.start_child( MyApp.Connection.Supervisor, client_id: SmartHose, server: {Tortoise311.Transport.Tcp, host: 'localhost', port: 1883}, handler: {Tortoise311.Handler.Logger, []} ) ``` -------------------------------- ### Start a Supervised Connection Source: https://context7.com/smartrent/tortoise311/llms.txt Use the dynamic supervisor to manage MQTT connections with automatic reconnection. Supports both TCP and SSL transport configurations. ```elixir # Start a supervised connection with TCP transport {:ok, pid} = Tortoise311.Supervisor.start_child( client_id: "my_client_id", handler: {Tortoise311.Handler.Logger, []}, server: {Tortoise311.Transport.Tcp, host: "localhost", port: 1883}, subscriptions: [{"sensors/+/temperature", 0}, {"alerts/#", 1}] ) # Start a connection with SSL transport using certifi for CA certificates {:ok, pid} = Tortoise311.Supervisor.start_child( client_id: "secure_client", handler: {MyApp.MQTTHandler, []}, server: { Tortoise311.Transport.SSL, host: "mqtt.example.com", port: 8883, cacertfile: :certifi.cacertfile(), certfile: 'client.crt', keyfile: 'client.key' }, subscriptions: [{"home/devices/#", 2}] ) ``` -------------------------------- ### Convert Atom to String for Client ID Source: https://github.com/smartrent/tortoise311/blob/main/docs/connecting_to_a_mqtt_broker.md Demonstrates converting an Elixir atom to a string for use as a client ID, showing the resulting string and its byte size. Note that atoms starting with an uppercase letter are prefixed with 'Elixir.' ```elixir iex(1)> client_id = Atom.to_string(MyClientId) "Elixir.MyClientId" iex(2)> byte_size(client_id) 17 ``` -------------------------------- ### Run Development Commands Source: https://github.com/smartrent/tortoise311/blob/main/README.md Fetches dependencies and executes the test suite for the project. ```bash mix deps.get MIX_ENV=test mix test ``` -------------------------------- ### Register and Handle MQTT Events Source: https://context7.com/smartrent/tortoise311/llms.txt Register for specific events and handle them within a GenServer process. ```elixir # Register for ping response times {:ok, _pid} = Tortoise311.Events.register("my_client_id", :ping_response) # Handle events in a GenServer def handle_info({{Tortoise311, "my_client_id"}, :status, :up}, state) do IO.puts("MQTT connection is up") {:noreply, %{state | connected: true}} end def handle_info({{Tortoise311, "my_client_id"}, :status, :down}, state) do IO.puts("MQTT connection is down") {:noreply, %{state | connected: false}} end def handle_info({{Tortoise311, "my_client_id"}, :ping_response, latency_ms}, state) do IO.puts("Ping latency: #{latency_ms}ms") {:noreply, state} end # Unregister when no longer needed :ok = Tortoise311.Events.unregister("my_client_id", :status) ``` -------------------------------- ### Publish Message with QoS 0 Source: https://github.com/smartrent/tortoise311/blob/main/docs/publishing_messages.md Publish a message with QoS 0. This method provides no guarantee of delivery and returns :ok immediately. ```iex iex(1)> Tortoise311.publish(MyClientId, "baz", nil, [qos: 0]) :ok ``` -------------------------------- ### Configure Telemetry Integration Source: https://context7.com/smartrent/tortoise311/llms.txt Define telemetry metrics or disable telemetry during supervisor startup. ```elixir # In your application's telemetry setup defmodule MyApp.Telemetry do def metrics do [ Telemetry.Metrics.sum("tortoise311.connection.tx_bytes"), Telemetry.Metrics.sum("tortoise311.connection.rx_bytes") ] end end # Disable telemetry if not needed (saves one process per connection) Tortoise311.Supervisor.start_child( client_id: "lightweight_client", handler: {Tortoise311.Handler.Logger, []}, server: {Tortoise311.Transport.Tcp, host: "localhost", port: 1883}, enable_telemetry: false ) ``` -------------------------------- ### Publish Message with QoS 1 and Receive Result Source: https://github.com/smartrent/tortoise311/blob/main/docs/publishing_messages.md Publish a message with QoS 1. This returns a reference, allowing for selective receive to confirm delivery. Use `flush()` in iex to view the result. ```iex iex(2)> Tortoise311.publish(MyClientId, "baz", nil, [qos: 1]) {:ok, #Reference<0.1924528969.904134659.102547>} ``` ```iex iex(3)> flush() {{Tortoise311, C}, #Reference<0.1924528969.904134659.102547>, :ok} :ok ``` -------------------------------- ### Publish Messages Asynchronously Source: https://context7.com/smartrent/tortoise311/llms.txt Publish messages to an MQTT broker. QoS 0 returns immediately, while QoS 1 and 2 return a reference for tracking delivery confirmation. ```elixir # Publish a QoS 0 message (fire and forget) :ok = Tortoise311.publish("my_client_id", "sensors/living_room/temp", "22.5", qos: 0) # Publish a retained message :ok = Tortoise311.publish("my_client_id", "devices/thermostat/setpoint", "21", qos: 0, retain: true) # Publish a QoS 1 message and receive confirmation asynchronously {:ok, ref} = Tortoise311.publish("my_client_id", "commands/device1", "reboot", qos: 1) # In a GenServer, handle the confirmation in handle_info def handle_info({{Tortoise311, "my_client_id"}, ref, :ok}, state) do IO.puts("Message delivered successfully") {:noreply, state} end # Publish a QoS 2 message (exactly once delivery) {:ok, ref} = Tortoise311.publish("my_client_id", "transactions/payment", Jason.encode!(%{amount: 100}), qos: 2) receive do {{Tortoise311, "my_client_id"}, ^ref, :ok} -> IO.puts("Transaction message confirmed") after 5000 -> IO.puts("Timeout waiting for confirmation") end ``` -------------------------------- ### Register for MQTT Connection Events Source: https://context7.com/smartrent/tortoise311/llms.txt Subscribe to connection status events using the Events PubSub module to monitor connection health and response times for metrics and alerting. ```elixir # Register for connection status events {:ok, _pid} = Tortoise311.Events.register("my_client_id", :status) ``` -------------------------------- ### Implement a Custom MQTT Message Handler Source: https://context7.com/smartrent/tortoise311/llms.txt Create a custom handler module to process incoming MQTT messages and respond to connection lifecycle events. Handlers run within the connection controller process. ```elixir defmodule MyApp.MQTTHandler do use Tortoise311.Handler defstruct [:db_conn, :message_count] @impl Tortoise311.Handler def init(args) do state = %__MODULE__{ db_conn: Keyword.get(args, :db_conn), message_count: 0 } {:ok, state} end @impl Tortoise311.Handler def connection(:up, state) do IO.puts("Connected to MQTT broker") # Optionally subscribe to additional topics when connection is up next_actions = [{:subscribe, "dynamic/topic", qos: 1}] {:ok, state, next_actions} end def connection(:down, state) do IO.puts("Disconnected from MQTT broker") {:ok, state} end @impl Tortoise311.Handler def handle_message(["sensors", device_id, "temperature"], payload, state) do temperature = String.to_float(payload) IO.puts("Device #{device_id} temperature: #{temperature}") {:ok, %{state | message_count: state.message_count + 1}} end def handle_message(["commands", device_id], payload, state) do IO.puts("Command for #{device_id}: #{payload}") # Unsubscribe after receiving command next_actions = [{:unsubscribe, "commands/#{device_id}"}] {:ok, state, next_actions} end def handle_message(topic, payload, state) do IO.puts("Received on #{Enum.join(topic, "/")}: #{inspect(payload)}") {:ok, state} end @impl Tortoise311.Handler def subscription(:up, topic_filter, state) do IO.puts("Subscribed to #{topic_filter}") {:ok, state} end def subscription({:warn, [requested: req, accepted: actual]}, topic_filter, state) do IO.puts("Subscription #{topic_filter} accepted with QoS #{actual} (requested #{req})") {:ok, state} end def subscription({:error, reason}, topic_filter, state) do IO.puts("Subscription #{topic_filter} rejected: #{inspect(reason)}") {:ok, state} end def subscription(:down, topic_filter, state) do IO.puts("Unsubscribed from #{topic_filter}") {:ok, state} end @impl Tortoise311.Handler def terminate(reason, state) do IO.puts("Handler terminating: #{inspect(reason)}, processed #{state.message_count} messages") :ok end end ``` -------------------------------- ### Configure SSL Connection Source: https://github.com/smartrent/tortoise311/blob/main/README.md Establishes an encrypted connection using SSL transport, requiring CA certificates and client credentials. ```elixir Tortoise311.Supervisor.start_child( client_id: "smart-spoon", handler: {Tortoise311.Handler.Logger, []}, server: { Tortoise311.Transport.SSL, host: host, port: port, cacertfile: :certifi.cacertfile(), key: key, cert: cert }, subscriptions: [{"foo/bar", 0}]) ``` -------------------------------- ### Subscribe to MQTT Topics Asynchronously Source: https://context7.com/smartrent/tortoise311/llms.txt Dynamically subscribe to single or multiple MQTT topics after establishing a connection. Supports topic filters with QoS levels. ```elixir # Asynchronous subscribe to a single topic {:ok, ref} = Tortoise311.Connection.subscribe("my_client_id", {"sensors/#", 1}) ``` ```elixir # Asynchronous subscribe to multiple topics {:ok, ref} = Tortoise311.Connection.subscribe("my_client_id", [ {"home/+/temperature", 0}, {"home/+/humidity", 0}, {"alerts/critical", 2} ]) ``` ```elixir # Subscribe with string topic and explicit QoS option {:ok, ref} = Tortoise311.Connection.subscribe("my_client_id", "news/sports", qos: 0) ``` -------------------------------- ### Tortoise311.publish/4 Source: https://github.com/smartrent/tortoise311/blob/main/docs/publishing_messages.md The `publish/4` function is the simplest way to send a message to an MQTT topic. It allows specifying the client ID, topic, an optional payload, and options like QoS and retain. ```APIDOC ## Tortoise311.publish/4 ### Description Publishes a message to an MQTT topic using a specified client connection. Supports optional payload and configuration options. ### Method `Tortoise311.publish/4` ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body - **client_id** (any) - Required - The identifier of the open MQTT connection. - **topic** (string) - Required - The MQTT topic to publish the message to. - **payload** (binary | nil) - Optional - The message payload. Defaults to `nil` if not provided. - **options** (list) - Optional - A list of options for the publish operation. - **qos** (integer) - Optional - The Quality of Service level (0, 1, or 2). Defaults to 0. - **retain** (boolean) - Optional - Whether the message should be retained by the broker. Defaults to `false`. ### Request Example ```elixir # Publish with payload and options Tortoise311.publish(MyClientId, "baz", "Hi !", [qos: 1, retain: true]) # Publish with options but no payload (nil payload) Tortoise311.publish(MyClientId, "baz", nil, [qos: 2]) # Publish with default options (QoS 0, no retain) Tortoise311.publish(MyClientId, "foo/bar", "hello, world !") ``` ### Response #### Success Response - **:ok** (atom) - Returned for messages published with QoS 0. - **{:ok, reference}** (tuple) - Returned for messages published with QoS 1 or 2, where `reference` is a unique identifier for the publish operation. #### Response Example ```iex # QoS 0 response iex(1)> Tortoise311.publish(MyClientId, "baz", nil, [qos: 0]) :ok # QoS 1 response iex(2)> Tortoise311.publish(MyClientId, "baz", nil, [qos: 1]) {:ok, #Reference<0.1924528969.904134659.102547>} # Handling QoS 1/2 response via flush (in iex) iex(3)> flush() {{Tortoise311, C}, #Reference<0.1924528969.904134659.102547>, :ok} :ok ``` ### Error Handling - Publishing with QoS > 0 returns a reference that can be used to track the publish status. - The response for QoS > 0 is a three-tuple: `{tortoise_connection_id, reference, result}`. ``` -------------------------------- ### Implement a Custom MQTT Handler Source: https://github.com/smartrent/tortoise311/blob/main/README.md Defines a custom handler module by implementing the Tortoise311.Handler behavior to process connection status and incoming messages. ```elixir defmodule Tortoise311.Handler.Example do use Tortoise311.Handler def init(args) do {:ok, args} end def connection(status, state) do # `status` will be either `:up` or `:down`; you can use this to # inform the rest of your system if the connection is currently # open or closed; tortoise should be busy reconnecting if you get # a `:down` {:ok, state} end # topic filter room/+/temp def handle_message(["room", room, "temp"], payload, state) do # :ok = Temperature.record(room, payload) {:ok, state} end def handle_message(topic, payload, state) do # unhandled message! You will crash if you subscribe to something # and you don't have a 'catch all' matcher; crashing on unexpected # messages could be a strategy though. {:ok, state} end def subscription(status, topic_filter, state) do {:ok, state} end def terminate(reason, state) do # tortoise doesn't care about what you return from terminate/2, # that is in alignment with other behaviours that implement a # terminate-callback :ok end end ``` -------------------------------- ### Subscribe/Unsubscribe with Next Actions in Controller Handler Source: https://github.com/smartrent/tortoise311/blob/main/CHANGELOG.md Allows specifying next actions like subscribing or unsubscribing to topics within the controller handler callback return tuple. This is useful for managing topic subscriptions dynamically based on events without blocking the controller. ```elixir {:ok, new_state, [{:subscribe, "foo/bar", qos: 3}, {:unsubscribe, "baz/quux"}]} ``` -------------------------------- ### Add Dependency to Mix Source: https://github.com/smartrent/tortoise311/blob/main/README.md Includes the library in the project dependencies within the mix.exs file. ```elixir def deps do [ {:tortoise311, "~> 0.11"} ] end ``` -------------------------------- ### Publish Message with Options and Nil Payload Source: https://github.com/smartrent/tortoise311/blob/main/docs/publishing_messages.md Send a message with a nil payload but specify options like QoS. Ensure nil is used explicitly for a nil payload when options are present. ```elixir Tortoise311.publish(MyClientId, "baz", nil, [qos: 2]) ``` -------------------------------- ### Subscribe to MQTT Topics Synchronously Source: https://context7.com/smartrent/tortoise311/llms.txt Perform a synchronous subscription to an MQTT topic, blocking until the server acknowledges the request or a timeout occurs. ```elixir # Synchronous subscribe - blocks until server acknowledges :ok = Tortoise311.Connection.subscribe_sync("my_client_id", {"events/important", 1}, timeout: 5000) ``` -------------------------------- ### Register Telemetry Metrics Source: https://github.com/smartrent/tortoise311/blob/main/README.md Registers byte count metrics for connections within the application startup process. ```elixir Telemetry.Metrics.sum("tortoise311.connection.tx_bytes") Telemetry.Metrics.sum("tortoise311.connection.rx_bytes") ``` -------------------------------- ### Configure a Last Will Message in Tortoise311 Source: https://github.com/smartrent/tortoise311/blob/main/docs/connecting_to_a_mqtt_broker.md Pass a Tortoise311.Package.Publish struct to the 'will' field during connection startup to define the message sent upon abrupt disconnection. ```elixir {:ok, pid} = Tortoise311.Connection.start_link( client_id: William, server: {Tortoise311.Transport.Tcp, host: 'localhost', port: 1883}, handler: {Tortoise311.Handler.Logger, []}, will: %Tortoise311.Package.Publish{topic: "foo/bar", payload: "goodbye"} ) ``` -------------------------------- ### Supervise a connection in a supervision tree Source: https://github.com/smartrent/tortoise311/blob/main/docs/connection_supervision.md Integrates a connection into an application's supervision tree using child_spec/1. This ensures the connection lifecycle is managed alongside the application. ```elixir defmodule MyApp.Supervisor do use Supervisor def start_link(opts) do Supervisor.start_link(__MODULE__, opts, name: __MODULE__) end @impl true def init(_opts) do children = [ {Tortoise311.Connection, [ client_id: WombatTaskForce, server: {Tortoise311.Transport.Tcp, host: 'localhost', port: 1883}, handler: {Tortoise311.Handler.Logger, []} ]} ] Supervisor.init(children, strategy: :one_for_one) end end ``` -------------------------------- ### Asynchronous Tortoise.subscribe/3 Source: https://github.com/smartrent/tortoise311/blob/main/CHANGELOG.md The Tortoise.subscribe/3 function is now asynchronous, sending a message to the calling process's mailbox. For synchronous behavior, use Tortoise.subscribe_sync/3. ```elixir Tortoise.subscribe/3 ``` -------------------------------- ### Efficiently Publish MQTT Messages with Tortoise311.Pipe Source: https://context7.com/smartrent/tortoise311/llms.txt Utilize Tortoise311.Pipe for high-performance MQTT message publishing by writing directly to the socket. This is recommended for high-throughput scenarios. ```elixir # Create a new pipe pipe = Tortoise311.Pipe.new("my_client_id") # Publish multiple messages efficiently pipe = Tortoise311.Pipe.publish(pipe, "telemetry/cpu", "45.2", qos: 0) pipe = Tortoise311.Pipe.publish(pipe, "telemetry/memory", "2048", qos: 0) pipe = Tortoise311.Pipe.publish(pipe, "telemetry/disk", "85", qos: 0) # Publish with QoS > 0 and await confirmation pipe = Tortoise311.Pipe.publish(pipe, "important/data", "critical_value", qos: 1) pipe = Tortoise311.Pipe.publish(pipe, "important/data2", "another_value", qos: 1) case Tortoise311.Pipe.await(pipe, 5000) do {:ok, pipe} -> IO.puts("All pending messages confirmed") {:error, :timeout} -> IO.puts("Timeout waiting for confirmations") end # Create an active pipe that receives connection updates pipe = Tortoise311.Pipe.new("my_client_id", active: true, timeout: 10_000) ``` -------------------------------- ### Ping the MQTT Broker Source: https://context7.com/smartrent/tortoise311/llms.txt Perform asynchronous or synchronous pings to verify connection health and measure latency. ```elixir # Asynchronous ping - result delivered to process mailbox {:ok, ref} = Tortoise311.Connection.ping("my_client_id") # Synchronous ping - blocks and returns latency case Tortoise311.Connection.ping_sync("my_client_id", 5000) do {:ok, latency_ms} -> IO.puts("Broker responded in #{latency_ms}ms") {:error, :timeout} -> IO.puts("Ping timeout - connection may be down") end ``` -------------------------------- ### Default SSL Verification in Tortoise.Transport.SSL Source: https://github.com/smartrent/tortoise311/blob/main/CHANGELOG.md The Tortoise.Transport.SSL module defaults to `[verify: :verify_peer]` for connections. Users should provide trusted CA certificates or explicitly set `verify: :verify_none` to opt out of peer verification. ```elixir [verify: :verify_peer] ``` -------------------------------- ### TCP Transport Specification Source: https://github.com/smartrent/tortoise311/blob/main/CHANGELOG.md Use the `{Tortoise.Transport.Tcp, host: 'localhost', port: 1883}` format for specifying the TCP transport and connection options. This replaces the older `{:tcp, 'localhost', 1883}` format. ```elixir {Tortoise.Transport.Tcp, host: 'localhost', port: 1883} ``` -------------------------------- ### Unsubscribe from MQTT Topics Synchronously Source: https://context7.com/smartrent/tortoise311/llms.txt Perform a synchronous unsubscribe from an MQTT topic, blocking until the server acknowledges the request or a timeout occurs. ```elixir # Synchronous unsubscribe - blocks until acknowledged :ok = Tortoise311.Connection.unsubscribe_sync("my_client_id", "temp/subscription", timeout: 5000) ``` -------------------------------- ### Publish Message with Payload Source: https://github.com/smartrent/tortoise311/blob/main/docs/publishing_messages.md Use this to send a message with a specific payload to a topic. The payload can be any binary data. ```elixir Tortoise311.publish(MyClientId, "foo/bar", "hello, world !") ``` -------------------------------- ### Publish Retained Message with QoS 1 Source: https://github.com/smartrent/tortoise311/blob/main/docs/publishing_messages.md Publish a message with a specific payload, setting the QoS to 1 and enabling the retain flag. This ensures the message is retained by the broker and delivered with a quality of service guarantee. ```elixir Tortoise311.publish(MyClientId, "baz", "Hi !", [qos: 1, retain: true]) ``` -------------------------------- ### SSL Transport Specification Source: https://github.com/smartrent/tortoise311/blob/main/CHANGELOG.md The SSL transport is specified using the format `{Tortoise.Transport.SSL, opts}`. This allows for encrypted connections to the MQTT broker. ```elixir {Tortoise.Transport.SSL, key: "path/to/key", cert: "path/to/cert"} ``` -------------------------------- ### Publish Messages Synchronously Source: https://context7.com/smartrent/tortoise311/llms.txt Block execution until the broker acknowledges receipt of the message. Useful for ensuring delivery before proceeding. ```elixir # Synchronous publish with QoS 1 case Tortoise311.publish_sync("my_client_id", "logs/critical", "System error detected", qos: 1, timeout: 5000) do :ok -> IO.puts("Critical log delivered") {:error, :timeout} -> IO.puts("Failed to deliver log - timeout") {:error, :unknown_connection} -> IO.puts("Connection not found") end # Synchronous publish with QoS 2 for guaranteed exactly-once delivery :ok = Tortoise311.publish_sync("my_client_id", "orders/new", Jason.encode!(%{order_id: "12345", items: ["widget", "gadget"]}), qos: 2, timeout: 10_000 ) ``` -------------------------------- ### Asynchronous Tortoise.unsubscribe/3 Source: https://github.com/smartrent/tortoise311/blob/main/CHANGELOG.md The Tortoise.unsubscribe/3 function is now asynchronous, sending a message to the calling process's mailbox. For synchronous behavior, use Tortoise.unsubscribe_sync/3. ```elixir Tortoise.unsubscribe/3 ``` -------------------------------- ### Disconnect from the Broker Source: https://context7.com/smartrent/tortoise311/llms.txt Gracefully terminate the connection and retrieve active subscriptions. ```elixir # Gracefully disconnect :ok = Tortoise311.Connection.disconnect("my_client_id") # Get current subscriptions before disconnecting subscriptions = Tortoise311.Connection.subscriptions("my_client_id") IO.inspect(subscriptions, label: "Active subscriptions") ``` -------------------------------- ### Embed Tortoise311.Supervisor in a custom supervisor Source: https://github.com/smartrent/tortoise311/blob/main/docs/connection_supervision.md Integrates the Tortoise311 supervisor into an application's own supervision tree to group connection lifecycles with the application. ```elixir defmodule MyApp.Supervisor do use Supervisor def start_link(opts) do Supervisor.start_link(__MODULE__, opts, name: __MODULE__) end @impl true def init(_opts) do children = [ {Tortoise311.Supervisor, [ name: MyApp.Connection.Supervisor, strategy: :one_for_one ]} ] Supervisor.init(children, strategy: :one_for_one) end end ``` -------------------------------- ### Unsubscribe from MQTT Topics Asynchronously Source: https://context7.com/smartrent/tortoise311/llms.txt Remove subscriptions from one or more MQTT topics when they are no longer needed. This operation is asynchronous. ```elixir # Asynchronous unsubscribe from a single topic {:ok, ref} = Tortoise311.Connection.unsubscribe("my_client_id", "sensors/old_device/#") ``` ```elixir # Asynchronous unsubscribe from multiple topics {:ok, ref} = Tortoise311.Connection.unsubscribe("my_client_id", ["alerts/test", "debug/#"]) ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.