feat(pm3py): auto-detect proxmark3 port when none specified
This commit is contained in:
@@ -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)
|
||||||
|
|||||||
Reference in New Issue
Block a user