Skip to main content

esphome_native_api/
connection.rs

1//! Handle to a live ESPHome API connection.
2
3use tokio::sync::{broadcast, mpsc, oneshot};
4
5use crate::error::Error;
6use crate::parser::ProtoMessage;
7
8/// A live connection to an ESPHome peer.
9///
10/// Returned by [`crate::esphomeapi::EspHomeApi::start`] and
11/// [`crate::esphomeserver::EspHomeServer::start`], this handle owns the channels
12/// used to talk to the peer and lets a consumer observe when — and why — the
13/// connection ends.
14///
15/// # Observing termination
16///
17/// Unlike a bare `(Sender, Receiver)` pair, a `Connection` reports its terminal
18/// outcome via [`Connection::wait`]. A [`Error::Disconnected`] result is the
19/// normal way a session ends and is usually not treated as a failure:
20///
21/// ```rust,no_run
22/// # use esphome_native_api::{esphomeapi::EspHomeApi, Error};
23/// # use tokio::net::TcpStream;
24/// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
25/// let stream = TcpStream::connect("192.168.1.100:6053").await?;
26/// let api = EspHomeApi::builder().name("client".to_string()).build()?;
27/// let connection = api.start(stream).await?;
28///
29/// let sender = connection.sender();
30/// let mut receiver = connection.receiver();
31///
32/// match connection.wait().await {
33///     Ok(()) | Err(Error::Disconnected(_)) => { /* peer left — expected */ }
34///     Err(e) => eprintln!("connection fault: {e}"),
35/// }
36/// # let _ = (sender, &mut receiver);
37/// # Ok(())
38/// # }
39/// ```
40#[derive(Debug)]
41pub struct Connection {
42    sender: mpsc::Sender<ProtoMessage>,
43    receiver: broadcast::Receiver<ProtoMessage>,
44    done: oneshot::Receiver<Result<(), Error>>,
45}
46
47impl Connection {
48    /// Construct a connection handle from its parts.
49    pub(crate) fn new(
50        sender: mpsc::Sender<ProtoMessage>,
51        receiver: broadcast::Receiver<ProtoMessage>,
52        done: oneshot::Receiver<Result<(), Error>>,
53    ) -> Self {
54        Self {
55            sender,
56            receiver,
57            done,
58        }
59    }
60
61    /// Returns a sender for messages to the peer. Can be called multiple times;
62    /// each call yields an independent, cloneable [`mpsc::Sender`].
63    pub fn sender(&self) -> mpsc::Sender<ProtoMessage> {
64        self.sender.clone()
65    }
66
67    /// Returns a receiver for messages from the peer. Each call yields a fresh
68    /// [`broadcast::Receiver`] that observes messages sent from this point on.
69    pub fn receiver(&self) -> broadcast::Receiver<ProtoMessage> {
70        self.receiver.resubscribe()
71    }
72
73    /// Wait for the connection to terminate and return its outcome.
74    ///
75    /// Returns `Ok(())` for a clean shutdown, `Err(Error::Disconnected(_))` when
76    /// the peer went away (the normal case), or another [`Error`] for a genuine
77    /// fault — including [`Error::TaskFailed`] if the connection's background
78    /// task died (for example, panicked) without reporting an outcome.
79    /// Consuming `self` here is deliberate: obtain a [`Connection::sender`]
80    /// and [`Connection::receiver`] first if you need them for the session.
81    pub async fn wait(self) -> Result<(), Error> {
82        self.done.await.unwrap_or(Err(Error::TaskFailed))
83    }
84}