fix(pm3py): platform-aware LED color mapping (Easy vs RDV4 have different physical layouts)
This commit is contained in:
50
pm3py/hw.py
50
pm3py/hw.py
@@ -73,6 +73,13 @@ class HWCommands:
|
||||
speed: Cycle time in ms (default 500)
|
||||
count: Repeat count, 0=infinite (default 5)
|
||||
"""
|
||||
# Fetch capabilities if not cached (needed for color mapping + PWM validation)
|
||||
if self._is_rdv4 is None:
|
||||
try:
|
||||
await self.capabilities()
|
||||
except PM3Error:
|
||||
pass # unknown platform, default to Easy mapping
|
||||
|
||||
# Resolve LED mask
|
||||
if isinstance(led, int):
|
||||
led_mask = led & 0x0F
|
||||
@@ -118,30 +125,41 @@ class HWCommands:
|
||||
"count": count if action_code in (4, 6) else None,
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def _parse_led_str(led: str) -> int:
|
||||
"""Parse LED string into bitmask. Accepts names, letters, or 'all'."""
|
||||
LED_MAP = {
|
||||
"a": 0x01, "b": 0x02, "c": 0x04, "d": 0x08,
|
||||
"green": 0x02, "orange": 0x01, "red": 0x04, "blue": 0x08,
|
||||
"all": 0x0F,
|
||||
}
|
||||
def _parse_led_str(self, led: str) -> int:
|
||||
"""Parse LED string into bitmask. Color mapping depends on platform.
|
||||
|
||||
Physical layout:
|
||||
RDV4: A=orange, B=green, C=red, D=red2
|
||||
PM3 Easy: A=green, B=red, C=orange, D=blue
|
||||
"""
|
||||
rdv4 = self._is_rdv4 # None if unknown, assume Easy
|
||||
# Letters always map directly
|
||||
LETTER_MAP = {"a": 0x01, "b": 0x02, "c": 0x04, "d": 0x08, "all": 0x0F}
|
||||
# Color names differ by platform
|
||||
COLOR_MAP_RDV4 = {"green": 0x02, "orange": 0x01, "red": 0x04, "red2": 0x08,
|
||||
"g": 0x02, "o": 0x01, "r": 0x04, "r2": 0x08}
|
||||
COLOR_MAP_EASY = {"green": 0x01, "orange": 0x04, "red": 0x02, "blue": 0x08,
|
||||
"g": 0x01, "o": 0x04, "r": 0x02, "b": 0x08}
|
||||
color_map = COLOR_MAP_RDV4 if rdv4 else COLOR_MAP_EASY
|
||||
|
||||
mask = 0
|
||||
for token in led.lower().replace(" ", "").split(","):
|
||||
if token in LED_MAP:
|
||||
mask |= LED_MAP[token]
|
||||
if token in LETTER_MAP:
|
||||
mask |= LETTER_MAP[token]
|
||||
elif token in color_map:
|
||||
mask |= color_map[token]
|
||||
elif token == "blue" and rdv4:
|
||||
raise ValueError("RDV4 has no blue LED")
|
||||
elif token in ("red2", "r2") and not rdv4:
|
||||
raise ValueError("PM3 Easy has no red2 LED, use 'blue' instead")
|
||||
else:
|
||||
raise ValueError(f"Unknown LED: {token!r}. Use: {', '.join(LED_MAP.keys())}")
|
||||
raise ValueError(f"Unknown LED: {token!r}. Use color names or letters a-d")
|
||||
return mask
|
||||
|
||||
async def _check_pwm_capable(self, led_mask: int, action: str) -> None:
|
||||
"""Raise if any LEDs in mask don't support PWM."""
|
||||
# Fetch capabilities if not cached
|
||||
if self._is_rdv4 is None:
|
||||
try:
|
||||
await self.capabilities()
|
||||
except PM3Error:
|
||||
return # can't validate, let it through
|
||||
return # can't validate, let it through
|
||||
|
||||
pwm_mask = self._PWM_LEDS_RDV4 if self._is_rdv4 else self._PWM_LEDS_EASY
|
||||
non_pwm = led_mask & ~pwm_mask
|
||||
|
||||
Reference in New Issue
Block a user