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}