esphome_native_api/
esphomeserver.rs

1use crate::esphomeapi::EspHomeApi;
2use crate::parser::ProtoMessage;
3
4use crate::proto::version_2025_12_1::ListEntitiesDoneResponse;
5use log::debug;
6use log::error;
7use log::info;
8use log::trace;
9use noise_protocol::CipherState;
10use noise_protocol::HandshakeState;
11use noise_rust_crypto::ChaCha20Poly1305;
12use noise_rust_crypto::Sha256;
13use noise_rust_crypto::X25519;
14use std::collections::HashMap;
15use std::str;
16use std::sync::Arc;
17use std::sync::atomic::AtomicBool;
18use tokio::net::TcpStream;
19use tokio::sync::Mutex;
20use tokio::sync::broadcast;
21use tokio::sync::mpsc;
22use typed_builder::TypedBuilder;
23
24#[derive(TypedBuilder)]
25pub struct EspHomeServer {
26    // Private fields
27    #[builder(default=HashMap::new(), setter(skip))]
28    pub(crate) components_by_key: HashMap<u32, Entity>,
29    #[builder(default=HashMap::new(), setter(skip))]
30    pub(crate) components_key_id: HashMap<String, u32>,
31    #[builder(default = 0, setter(skip))]
32    pub(crate) current_key: u32,
33
34    #[builder(via_mutators, default=Arc::new(AtomicBool::new(false)))]
35    pub(crate) encrypted_api: Arc<AtomicBool>,
36
37    #[builder(via_mutators)]
38    pub(crate) noise_psk: Vec<u8>,
39
40    #[builder(default=Arc::new(Mutex::new(None)), setter(skip))]
41    pub(crate) handshake_state:
42        Arc<Mutex<Option<HandshakeState<X25519, ChaCha20Poly1305, Sha256>>>>,
43    #[builder(default=Arc::new(Mutex::new(None)), setter(skip))]
44    pub(crate) encrypt_cypher: Arc<Mutex<Option<CipherState<ChaCha20Poly1305>>>>,
45    #[builder(default=Arc::new(Mutex::new(None)), setter(skip))]
46    pub(crate) decrypt_cypher: Arc<Mutex<Option<CipherState<ChaCha20Poly1305>>>>,
47
48    name: String,
49
50    #[builder(default = None, setter(strip_option))]
51    #[deprecated(note = "https://esphome.io/components/api.html#configuration-variables")]
52    password: Option<String>,
53    #[builder(default = None, setter(strip_option))]
54    encryption_key: Option<String>,
55
56    #[builder(default = 1)]
57    api_version_major: u32,
58    #[builder(default = 10)]
59    api_version_minor: u32,
60    #[builder(default="Rust: esphome-native-api".to_string())]
61    server_info: String,
62
63    #[builder(default = None, setter(strip_option))]
64    friendly_name: Option<String>,
65
66    #[builder(default = None, setter(strip_option))]
67    mac: Option<String>,
68
69    #[builder(default = None, setter(strip_option))]
70    model: Option<String>,
71
72    #[builder(default = None, setter(strip_option))]
73    manufacturer: Option<String>,
74    #[builder(default = None, setter(strip_option))]
75    suggested_area: Option<String>,
76    #[builder(default = None, setter(strip_option))]
77    bluetooth_mac_address: Option<String>,
78}
79
80impl EspHomeServer {
81    /// Easier Version of the api abstraction
82    /// Handles:
83    /// - internal entity key management
84
85    pub async fn start(
86        &mut self,
87        tcp_stream: TcpStream,
88    ) -> Result<
89        (
90            mpsc::Sender<ProtoMessage>,
91            broadcast::Receiver<ProtoMessage>,
92        ),
93        Box<dyn std::error::Error>,
94    > {
95        let mut server = EspHomeApi::builder()
96            .api_version_major(self.api_version_major)
97            .api_version_minor(self.api_version_minor)
98            // .password(self.password.or_else())
99            .server_info(self.server_info.clone())
100            .name(self.name.clone())
101            // .friendly_name(self.friendly_name)
102            // .bluetooth_mac_address(self.bluetooth_mac_address)
103            // .mac(self.mac)
104            // .manufacturer(self.manufacturer)
105            // .model(self.model)
106            // .suggested_area(self.suggested_area)
107            .build();
108        let (messages_tx, mut messages_rx) = server.start(tcp_stream).await?;
109        let (outgoing_messages_tx, mut outgoing_messages_rx) =
110            broadcast::channel::<ProtoMessage>(16);
111        let api_components_clone = self.components_by_key.clone();
112        let messages_tx_clone = messages_tx.clone();
113
114        tokio::spawn(async move {
115            loop {
116                messages_rx.recv().await.map_or_else(
117                    |e| {
118                        error!("Error receiving message: {:?}", e);
119                        // Handle the error, maybe log it or break the loop
120                    },
121                    |message| {
122                        // Process the received message
123                        debug!("Received message: {:?}", message);
124
125                        match message {
126                            ProtoMessage::ListEntitiesRequest(list_entities_request) => {
127                                debug!("ListEntitiesRequest: {:?}", list_entities_request);
128
129                                for (_key, _sensor) in &api_components_clone {
130                                    // TODO: Handle the different entity types
131                                    // outgoing_messages_tx.send(sensor.clone()).unwrap();
132                                }
133                                outgoing_messages_tx
134                                    .send(ProtoMessage::ListEntitiesDoneResponse(
135                                        ListEntitiesDoneResponse {},
136                                    ))
137                                    .unwrap();
138                            }
139                            other_message => {
140                                // Forward the message to the outgoing channel
141                                if let Err(e) = outgoing_messages_tx.send(other_message) {
142                                    error!("Error sending message to outgoing channel: {:?}", e);
143                                }
144                            }
145                        }
146                    },
147                );
148            }
149        });
150
151        Ok((messages_tx.clone(), outgoing_messages_rx))
152    }
153
154    pub fn add_entity(&mut self, entity_id: &str, entity: Entity) {
155        self.components_key_id
156            .insert(entity_id.to_string(), self.current_key);
157        self.components_by_key.insert(self.current_key, entity);
158
159        self.current_key += 1;
160    }
161}
162
163#[derive(Clone, Debug)]
164pub enum Entity {
165    BinarySensor(BinarySensor),
166}
167
168#[derive(Clone, Debug)]
169pub struct BinarySensor {
170    pub object_id: String,
171}