Welcome! An instance of USMPSession represents a fully established, secure USMP connection.
On the server side, a session object is passed directly into your registered @server.on_session callback handler. On the client side, the session logic is wrapped under the hood inside USMPClient. This guide explains the properties, interactive methods, and reassembly features of the session object.
Properties
You can query these read-only properties to identify the connected device and session details:
session.device_id(str): The client's unique hardware address, formatted as a hexadecimal string with colons (e.g.,"00:11:22:33:44:55").session.session_id(str): A unique session ID generated by the server during the handshake, formatted as a 32-character hex string.
Interactive Methods
await session.send(data: bytes)
Encrypts and transmits a payload to the device.
- Payload Fragmentation: If your payload exceeds the single-frame plaintext limit of 452 bytes,
send()automatically fragments it into multiple chunks. The initial chunks are transmitted asPKT_DATA_FRAGframes, followed by a finalPKT_DATAframe. - Size Constraint: The dynamic fragmenter supports up to 4 frames, giving an absolute maximum payload size of 1808 bytes (~1.8 KB). Attempting to send a larger payload raises a
PayloadErrorexception.
await session.recv(timeout: float | None = None) -> bytes
Blocks until the next decrypted message payload is received and verified.
- Payload Reassembly: If the incoming message was fragmented,
recv()transparently accumulates thePKT_DATA_FRAGframes, decrypts each piece, and joins them back together. It returns the complete, reassembled plaintext once the finalPKT_DATAframe arrives. - Control Frame Filtering: If a client sends a keepalive
PINGframe during an idle period,recv()intercepts it, automatically replies with aPONGframe, and resumes waiting without waking up your application code. - Reassembly Safeguards: If a control frame (like
PINGorBYE) is interleaved while reassembly is in progress, or if the reassembly requires more than 4 fragments, the session state machine rejects the stream, raises aSequenceErrororPayloadError, and closes the connection. - Timeout: If a custom
timeout(in seconds) is specified and no frame is received within that duration, aTimeoutErroris raised.
await session.ping()
Pushes an encrypted PKT_PING frame to the device. This is useful for checking if a device is still online and resetting the socket watchdog.
await session.bye()
Sends an encrypted PKT_BYE frame to notify the device of a clean shutdown, then closes the TCP connection.
Complete Session Reader/Writer Example
The standard way to manage a session is to run session.recv() inside a structured while True loop to process commands and handle timeouts:
from usmp import USMPSession
from usmp.errors import ConnectionClosedError, CryptoError, TimeoutError
async def handle_device_session(session: USMPSession):
print(f"[START] Monitoring session for device: {session.device_id}")
try:
while True:
# Set a 60-second read timeout. If the client goes silent (fails
# to send telemetry or a PING) for 60 seconds, we close the link.
data = await session.recv(timeout=60.0)
# Simple command parser
if data == b"GET_STATUS":
await session.send(b"STATUS_OK")
elif data == b"SHUTDOWN":
print(f"[{session.device_id}] Requested graceful exit.")
await session.send(b"BYE_ACK")
await session.bye()
break
else:
print(f"[{session.device_id}] Received data: {data!r}")
except TimeoutError:
print(f"[TIMEOUT] Device {session.device_id} went offline (no keepalives).")
except ConnectionClosedError:
print(f"[CLOSED] Device {session.device_id} disconnected cleanly.")
except CryptoError as e:
print(f"[CRYPTO ERROR] Decryption failed for {session.device_id}: {e}")
finally:
print(f"[STOP] Session handler terminated for {session.device_id}")