Python bindings for USB Power Delivery protocol message parsing
Project description
usbpdpy
Python bindings for USB Power Delivery message parsing using the usbpd Rust crate.
Features
- Parse USB PD messages with full specification compliance
- Support for Source Capabilities and Request message parsing
- All PDO types: Fixed Supply, Battery, Variable Supply, PPS, EPR
- Complete RDO (Request Data Object) parsing with PDO state management
- Message header parsing with proper control/data message classification
- Python type hints and error handling
Installation
pip install usbpdpy
Quick Start
Parse Source Capabilities
import usbpdpy
# Parse a Source Capabilities message
source_caps_hex = "a1612c9101082cd102002cc103002cb10400454106003c21dcc0"
source_caps_bytes = bytes.fromhex(source_caps_hex)
message = usbpdpy.parse_pd_message(source_caps_bytes)
print(f"Message: {message.header.message_type}")
print(f"Power role: {message.header.port_power_role}")
print(f"Available PDOs: {len(message.data_objects)}")
for i, pdo in enumerate(message.data_objects):
print(f" PDO {i+1}: {pdo}")
# Output: PDO 1: PowerDataObj(FixedSupply: 5V @ 3A = 15W)
# PDO 2: PowerDataObj(FixedSupply: 9V @ 3A = 27W)
# ...
Parse Request Messages
# First, parse Source Capabilities to get PDO state
source_msg = usbpdpy.parse_pd_message(source_caps_bytes)
# Parse Request message with PDO context
request_hex = "8210dc700323"
request_bytes = bytes.fromhex(request_hex)
request_msg = usbpdpy.parse_pd_message_with_state(request_bytes, source_msg.data_objects)
print(f"Request type: {request_msg.header.message_type}")
for rdo in request_msg.request_objects:
print(f" Requesting PDO #{rdo.object_position}: {rdo.rdo_type}")
# Output: Requesting PDO #2: FixedVariableSupply
API Reference
Core Functions
parse_pd_message(data: bytes) -> PdMessage- Parse a USB PD messageparse_pd_message_with_state(data: bytes, pdo_state: List[PowerDataObj]) -> PdMessage- Parse with PDO context for Request messagesparse_messages(messages: List[bytes]) -> List[PdMessage]- Parse multiple messagesget_message_type_name(msg_type: int, num_objects: int) -> str- Get human-readable message typehex_to_bytes(hex_str: str) -> List[int]- Convert hex string to byte listbytes_to_hex(data: bytes) -> str- Convert bytes to hex string
Message Structure
PdMessage
header: PdHeader- Message header informationdata_objects: List[PowerDataObj]- Power Data Objects (PDOs) for Source/Sink Capabilitiesrequest_objects: List[RequestDataObj]- Request Data Objects (RDOs) for Request messagesraw_bytes: bytes- Original message bytesis_control_message() -> bool- Check if control messageis_data_message() -> bool- Check if data messageis_source_capabilities() -> bool- Check if Source Capabilitieshex() -> str- Get message as hex string
PdHeader
message_type: str- Human-readable type (e.g., "Source_Capabilities", "GoodCRC")message_type_raw: int- Raw type code (0–31)port_data_role: str- "Ufp" or "Dfp"port_power_role: str- "Sink" or "Source"message_id: int- Message ID (0–7)num_data_objects: int- Number of 32-bit data objects (0–7)spec_revision: int- PD spec rev (0=R1.0, 1=R2.0, 2=R3.0)extended: bool- Extended message flag
PowerDataObj
pdo_type: str- "FixedSupply", "Battery", "VariableSupply", "PPS", "EPR_AVS", "Unknown"raw: int- Raw 32-bit PDO valuevoltage_v: Optional[float]- Voltage in volts (fixed supply)max_current_a: Optional[float]- Maximum current in amperesmax_power_w: Optional[float]- Maximum power in watts (calculated or battery)min_voltage_v: Optional[float]- Minimum voltage in volts (variable/PPS)max_voltage_v: Optional[float]- Maximum voltage in volts (variable/PPS)dual_role_power: Optional[bool]- Dual role power capabilityusb_communications_capable: Optional[bool]- USB communications capabilityunconstrained_power: Optional[bool]- Unconstrained power flag
RequestDataObj
rdo_type: str- "FixedVariableSupply", "Battery", "PPS", "AVS", "Unknown"raw: int- Raw 32-bit RDO valueobject_position: int- PDO position being requested (1-7)operating_current_a: Optional[float]- Operating current in amperesmax_operating_current_a: Optional[float]- Maximum operating current in amperesoperating_voltage_v: Optional[float]- Operating voltage in volts (PPS/AVS)operating_power_w: Optional[float]- Operating power in watts (battery)max_operating_power_w: Optional[float]- Maximum operating power in watts (battery)capability_mismatch: bool- Capability mismatch flagusb_communications_capable: bool- USB communications capabilityno_usb_suspend: bool- No USB suspend flaggiveback_flag: Optional[bool]- GiveBack flag (fixed/variable supply)
Message Types
The library correctly distinguishes between control and data messages:
- Control Messages (
num_data_objects = 0): GoodCRC, Accept, Reject, PS_RDY, etc. - Data Messages (
num_data_objects > 0): Source_Capabilities, Request, Sink_Capabilities, etc.
Supported Data Messages
- Source_Capabilities: Parsed into
data_objects(PDOs) - Request: Parsed into
request_objects(RDOs) when PDO state is provided - Sink_Capabilities: Header parsed, data objects pending
- Other data messages: Header parsed, data objects pending
Real-World Usage
This library has been tested with real USB PD captures from KM003C hardware, including complete negotiation sequences:
# Parse a complete USB PD negotiation sequence
messages = [
"a1612c9101082cd102002cc103002cb10400454106003c21dcc0", # Source_Capabilities
"8210dc700323", # Request (9V)
"a305", # Accept
"a607", # PS_RDY
]
source_msg = usbpdpy.parse_pd_message(bytes.fromhex(messages[0]))
request_msg = usbpdpy.parse_pd_message_with_state(
bytes.fromhex(messages[1]),
source_msg.data_objects
)
print(f"Sink requested PDO #{request_msg.request_objects[0].object_position}")
# Output: Sink requested PDO #2 (9V from the source capabilities)
Development
# Install development dependencies
uv sync --dev
# Build the extension
uv run maturin develop
# Run tests
uv run pytest
# Lint and format
uv run ruff check .
uv run ruff format .
Requirements
- Python 3.8+
- No runtime dependencies
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file usbpdpy-0.2.1.tar.gz.
File metadata
- Download URL: usbpdpy-0.2.1.tar.gz
- Upload date:
- Size: 32.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: maturin/1.9.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d78b2ef3006543661e6c1cd2dfc622d9f51e8356479126e0003ee9bc331cb5b7
|
|
| MD5 |
c883144835d75df071cc590a99885604
|
|
| BLAKE2b-256 |
df08e928505b4e0e2265cf4ef51cd785640ed6bb0d2e654f498199d93041c599
|
File details
Details for the file usbpdpy-0.2.1-cp312-cp312-manylinux_2_34_x86_64.whl.
File metadata
- Download URL: usbpdpy-0.2.1-cp312-cp312-manylinux_2_34_x86_64.whl
- Upload date:
- Size: 272.2 kB
- Tags: CPython 3.12, manylinux: glibc 2.34+ x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: maturin/1.9.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f542754e7a1231789276703589ce96ad62c5e6df17946ebe810e77a520baa9f4
|
|
| MD5 |
06fca9dc12a71ebaaf3267c739a85a0f
|
|
| BLAKE2b-256 |
4dff75af73eed618397ace3877cbd90227d0463af1bb2fc846144891af4e773c
|