feat(pm3py): auto-detect proxmark3 port when none specified

This commit is contained in:
michael
2026-03-16 00:10:50 -07:00
parent 205e9b7993
commit 29bfccfb41

View File

@@ -1,7 +1,9 @@
"""Main Proxmark3 client with nested API.""" """Main Proxmark3 client with nested API."""
import asyncio import asyncio
import glob
import inspect import inspect
import logging import logging
import sys
from dataclasses import dataclass, field from dataclasses import dataclass, field
from .transport import PM3Transport, PM3Response, PM3Error, ProgressCallback from .transport import PM3Transport, PM3Response, PM3Error, ProgressCallback
@@ -26,21 +28,42 @@ class FirmwareInfo:
warnings: list[str] = field(default_factory=list) warnings: list[str] = field(default_factory=list)
def find_proxmark_port() -> str | None:
"""Auto-detect the first available Proxmark3 serial port."""
if sys.platform == "darwin":
patterns = ["/dev/tty.usbmodem*"]
elif sys.platform == "win32":
# On Windows, we can't glob — caller should specify port
return None
else:
patterns = ["/dev/ttyACM*", "/dev/ttyUSB*"]
for pattern in patterns:
ports = sorted(glob.glob(pattern))
if ports:
return ports[0]
return None
class Proxmark3: class Proxmark3:
"""Proxmark3 device client with structured Python API. """Proxmark3 device client with structured Python API.
Usage: Usage:
async with Proxmark3("/dev/ttyACM0") as pm3: async with Proxmark3() as pm3: # auto-detect port
if not pm3.firmware.compatible: print(await pm3.hw.ping())
print("Warnings:", pm3.firmware.warnings)
status = await pm3.hw.version()
print(status)
# Raw access: async with Proxmark3("/dev/ttyACM0") as pm3: # explicit port
resp = await pm3.send_ng(0x0108) resp = await pm3.send_ng(0x0108)
pm3 = Proxmark3.sync() # sync REPL, auto-detect
""" """
def __init__(self, port: str, baudrate: int = 115200): def __init__(self, port: str | None = None, baudrate: int = 115200):
if port is None:
port = find_proxmark_port()
if port is None:
raise PM3Error("No Proxmark3 device found")
log.info("pm3py: auto-detected %s", port)
self._transport = PM3Transport(port, baudrate) self._transport = PM3Transport(port, baudrate)
self.hw = HWCommands(self._transport) self.hw = HWCommands(self._transport)
self.lf = LFCommands(self._transport) self.lf = LFCommands(self._transport)
@@ -106,14 +129,14 @@ class Proxmark3:
return await self._transport.send_mix(cmd, arg0, arg1, arg2, payload, timeout) return await self._transport.send_mix(cmd, arg0, arg1, arg2, payload, timeout)
@classmethod @classmethod
def sync(cls, port: str, baudrate: int = 115200) -> "Proxmark3": def sync(cls, port: str | None = None, baudrate: int = 115200) -> "Proxmark3":
"""Create a synchronous client for REPL / script usage. """Create a synchronous client for REPL / script usage.
Usage: Usage:
pm3 = Proxmark3.sync("/dev/ttyACM0") pm3 = Proxmark3.sync() # auto-detect
pm3 = Proxmark3.sync("/dev/ttyACM0") # explicit
print(pm3.firmware) print(pm3.firmware)
print(pm3.hw.ping()) print(pm3.hw.ping())
print(pm3.hf.mf.rdbl(block=0))
pm3.close() pm3.close()
""" """
instance = cls(port, baudrate) instance = cls(port, baudrate)