Python SDK: Session Reference (`USMPSession`)

Welcome! An instance of `USMPSession` represents a fully established, secure USMP connection.

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 as PKT_DATA_FRAG frames, followed by a final PKT_DATA frame.
  • 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 PayloadError exception.

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 the PKT_DATA_FRAG frames, decrypts each piece, and joins them back together. It returns the complete, reassembled plaintext once the final PKT_DATA frame arrives.
  • Control Frame Filtering: If a client sends a keepalive PING frame during an idle period, recv() intercepts it, automatically replies with a PONG frame, and resumes waiting without waking up your application code.
  • Reassembly Safeguards: If a control frame (like PING or BYE) is interleaved while reassembly is in progress, or if the reassembly requires more than 4 fragments, the session state machine rejects the stream, raises a SequenceError or PayloadError, and closes the connection.
  • Timeout: If a custom timeout (in seconds) is specified and no frame is received within that duration, a TimeoutError is 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}")