knx_rs_ip/lib.rs
1// SPDX-License-Identifier: GPL-3.0-only
2// Copyright (C) 2026 Fabian Schmieder
3
4//! `knx-ip` — async KNXnet/IP tunnel and router connections.
5//!
6//! This crate provides a complete async KNXnet/IP implementation:
7//!
8//! - [`TunnelConnection`] — unicast tunnel with retry, heartbeat, auto-reconnect
9//! - [`RouterConnection`] — multicast routing with rate limiting (50 pkt/s)
10//! - [`discovery`] — gateway discovery on the local network
11//! - [`Multiplexer`] / [`MultiplexHandle`] — fan out one connection to many handles
12//! - [`connect`] / [`parse_url`] — URL-based connection factory
13//!
14//! # Example
15//!
16//! ```rust,no_run
17//! use knx_rs_ip::{connect, ConnectionSpec};
18//!
19//! # async fn example() -> Result<(), Box<dyn std::error::Error>> {
20//! let spec = ConnectionSpec::Tunnel("192.168.1.50:3671".parse()?);
21//! let mut conn = connect(spec).await?;
22//! // conn.send(...) / conn.recv() ...
23//! # Ok(())
24//! # }
25//! ```
26
27mod error;
28mod router;
29mod tunnel;
30pub mod tunnel_server;
31mod url;
32
33pub mod discovery;
34pub mod multiplex;
35pub mod ops;
36
37pub use error::KnxIpError;
38pub use multiplex::{MultiplexHandle, Multiplexer};
39pub use router::{KNX_MULTICAST_ADDR, KNX_PORT, RouterConnection};
40pub use tunnel::{TunnelConfig, TunnelConnection};
41pub use tunnel_server::{DeviceServer, ServerEvent};
42pub use url::{ConnectionSpec, connect, parse_url};
43
44use core::future::Future;
45use core::pin::Pin;
46
47use knx_rs_core::cemi::CemiFrame;
48
49/// Boxed `Send` future returned by KNX connection traits.
50pub type KnxFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;
51
52/// Trait for KNXnet/IP connections that can send and receive CEMI frames.
53///
54/// Both tunnel (unicast) and router (multicast) connections implement this.
55pub trait KnxConnection: Send {
56 /// Send a CEMI frame to the KNX bus.
57 ///
58 /// # Errors
59 ///
60 /// Returns [`KnxIpError`] if the frame could not be sent.
61 fn send(&self, frame: CemiFrame) -> KnxFuture<'_, Result<(), KnxIpError>>;
62
63 /// Receive the next CEMI frame from the KNX bus.
64 ///
65 /// Returns `None` if the connection is closed.
66 fn recv(&mut self) -> KnxFuture<'_, Option<CemiFrame>>;
67
68 /// Close the connection gracefully.
69 fn close(&mut self) -> KnxFuture<'_, ()>;
70}
71
72/// A type-erased KNX connection — either tunnel or router.
73///
74/// Returned by [`connect()`] when the connection type is determined at runtime.
75pub enum AnyConnection {
76 /// Tunnel connection.
77 Tunnel(TunnelConnection),
78 /// Router connection.
79 Router(RouterConnection),
80}
81
82impl KnxConnection for AnyConnection {
83 fn send(&self, frame: CemiFrame) -> KnxFuture<'_, Result<(), KnxIpError>> {
84 match self {
85 Self::Tunnel(c) => c.send(frame),
86 Self::Router(c) => c.send(frame),
87 }
88 }
89
90 fn recv(&mut self) -> KnxFuture<'_, Option<CemiFrame>> {
91 match self {
92 Self::Tunnel(c) => c.recv(),
93 Self::Router(c) => c.recv(),
94 }
95 }
96
97 fn close(&mut self) -> KnxFuture<'_, ()> {
98 match self {
99 Self::Tunnel(c) => c.close(),
100 Self::Router(c) => c.close(),
101 }
102 }
103}