diff --git a/pm3py/client.py b/pm3py/client.py index 0d78e17..1285ec2 100644 --- a/pm3py/client.py +++ b/pm3py/client.py @@ -1,7 +1,9 @@ """Main Proxmark3 client with nested API.""" import asyncio +import glob import inspect import logging +import sys from dataclasses import dataclass, field from .transport import PM3Transport, PM3Response, PM3Error, ProgressCallback @@ -26,21 +28,42 @@ class FirmwareInfo: 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: """Proxmark3 device client with structured Python API. Usage: - async with Proxmark3("/dev/ttyACM0") as pm3: - if not pm3.firmware.compatible: - print("Warnings:", pm3.firmware.warnings) - status = await pm3.hw.version() - print(status) + async with Proxmark3() as pm3: # auto-detect port + print(await pm3.hw.ping()) - # Raw access: + async with Proxmark3("/dev/ttyACM0") as pm3: # explicit port 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.hw = HWCommands(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) @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. Usage: - pm3 = Proxmark3.sync("/dev/ttyACM0") + pm3 = Proxmark3.sync() # auto-detect + pm3 = Proxmark3.sync("/dev/ttyACM0") # explicit print(pm3.firmware) print(pm3.hw.ping()) - print(pm3.hf.mf.rdbl(block=0)) pm3.close() """ instance = cls(port, baudrate)