Files
pm3py/pm3py/cli/rawcli/session.py
michael e024294ec6 feat(rawcli): identify + persistent connection (Phase 3)
- identify.py: probe HF (14a -> 15693) then LF; set session field /
  protocol / transponder. The 14a scan uses NO_DISCONNECT so the tag
  stays selected (the persistent connection). SAK -> coarse family name
  for the breadcrumb.
- session: add `protocol` (drives raw-exchange routing)
- app: connect via Proxmark3.sync() (scan/raw become plain calls); wire
  identify / transponder / close control commands; protocol-aware
  _raw_exchange; breadcrumb fills in field + transponder after identify

8 new tests (identify 14a/15693/none, clear, control commands). Device
probes are mocked; live identify is the user's hardware test.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 12:10:51 -07:00

46 lines
1.7 KiB
Python

"""rawcli session state — the connected device, the identified RF field and transponder, and
the current numeric entry mode. Kept deliberately small and free of any prompt_toolkit / device
detail so it stays unit-testable."""
from __future__ import annotations
from typing import Any
HEX = "hex"
BIN = "bin"
class RawSession:
"""Mutable rawcli session state.
``device`` is the connected client (or ``None`` when offline). ``field`` is the identified RF
field (``"hf"``/``"lf"``) and ``transponder`` the identified tag's display name — both set by
``identify`` in a later phase. ``entry_mode`` is the numeric entry mode toggled by the user.
"""
def __init__(self, device: Any = None):
self.device = device
self.field: str | None = None
self.transponder: str | None = None
self.protocol: str = "hf14a" # wire protocol for raw exchanges (set by identify)
self.entry_mode: str = HEX
self.connected: bool = device is not None
@property
def entry_prefix(self) -> str:
return "0x" if self.entry_mode == HEX else "0b"
def toggle_entry_mode(self) -> str:
self.entry_mode = BIN if self.entry_mode == HEX else HEX
return self.entry_mode
def breadcrumb(self) -> str:
"""The segmented prompt ``[raw / <field?> / <transponder?> / 0x|0b]`` — field and
transponder segments appear only once identified."""
segments = ["raw"]
if self.field:
segments.append(self.field)
if self.transponder:
segments.append(self.transponder)
segments.append(self.entry_prefix)
return "[" + " / ".join(segments) + "]"