Compare commits
2 Commits
8df1d9dd81
...
3844aa9d6a
| Author | SHA1 | Date | |
|---|---|---|---|
| 3844aa9d6a | |||
| d6ed6ad9f5 |
77
cli.py
77
cli.py
@@ -139,6 +139,26 @@ def _get_ordered_actions(argv: List[str]) -> List[tuple]:
|
|||||||
if vals:
|
if vals:
|
||||||
actions.append(('upload', vals))
|
actions.append(('upload', vals))
|
||||||
continue
|
continue
|
||||||
|
if arg == '--src':
|
||||||
|
# Upload local DIR (default: ./src) to device root (:/
|
||||||
|
local_dir = "src"
|
||||||
|
if i + 1 < len(argv) and not argv[i + 1].startswith('-'):
|
||||||
|
local_dir = argv[i + 1]
|
||||||
|
i += 2
|
||||||
|
else:
|
||||||
|
i += 1
|
||||||
|
# Use empty string as remote_dir to map to root on device
|
||||||
|
actions.append(('upload', [local_dir, ""]))
|
||||||
|
continue
|
||||||
|
if arg == '--lib':
|
||||||
|
# Upload local DIR to /lib on device
|
||||||
|
if i + 1 < len(argv):
|
||||||
|
local_dir = argv[i + 1]
|
||||||
|
actions.append(('upload', [local_dir, "lib"]))
|
||||||
|
i += 2
|
||||||
|
else:
|
||||||
|
raise ValueError("--lib requires a directory argument")
|
||||||
|
continue
|
||||||
if arg == '-e':
|
if arg == '-e':
|
||||||
actions.append(('erase_all', None))
|
actions.append(('erase_all', None))
|
||||||
i += 1
|
i += 1
|
||||||
@@ -151,6 +171,17 @@ def _get_ordered_actions(argv: List[str]) -> List[tuple]:
|
|||||||
raise ValueError("--rm requires an argument")
|
raise ValueError("--rm requires an argument")
|
||||||
continue
|
continue
|
||||||
if arg in ('-f', '--follow'):
|
if arg in ('-f', '--follow'):
|
||||||
|
# Optional duration in seconds: --follow [SECONDS]
|
||||||
|
follow_secs = None
|
||||||
|
if i + 1 < len(argv) and not argv[i + 1].startswith('-'):
|
||||||
|
try:
|
||||||
|
follow_secs = float(argv[i + 1])
|
||||||
|
i += 2
|
||||||
|
except ValueError:
|
||||||
|
# Not a number, treat as flag-only
|
||||||
|
i += 1
|
||||||
|
actions.append(('follow', follow_secs))
|
||||||
|
else:
|
||||||
actions.append(('follow', None))
|
actions.append(('follow', None))
|
||||||
i += 1
|
i += 1
|
||||||
continue
|
continue
|
||||||
@@ -212,6 +243,14 @@ Examples:
|
|||||||
help="Device name"
|
help="Device name"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
"--id",
|
||||||
|
dest="device_id",
|
||||||
|
type=int,
|
||||||
|
metavar="0-255",
|
||||||
|
help="Numeric device ID used for ESPNow (0-255)"
|
||||||
|
)
|
||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--pin",
|
"--pin",
|
||||||
type=int,
|
type=int,
|
||||||
@@ -258,7 +297,7 @@ Examples:
|
|||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--default",
|
"--default",
|
||||||
metavar="PATTERN",
|
metavar="PATTERN",
|
||||||
help="Default/startup pattern (startup_preset in settings.json)"
|
help="Default/startup pattern (stored as 'default' in settings.json)"
|
||||||
)
|
)
|
||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
@@ -276,8 +315,11 @@ Examples:
|
|||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"-f", "--follow",
|
"-f", "--follow",
|
||||||
action="store_true",
|
nargs="?",
|
||||||
help="Follow device output continuously (like tail -f)"
|
const=None,
|
||||||
|
type=float,
|
||||||
|
metavar="SECONDS",
|
||||||
|
help="Follow device output continuously (like tail -f). Optionally specify SECONDS to limit follow duration."
|
||||||
)
|
)
|
||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
@@ -293,6 +335,20 @@ Examples:
|
|||||||
help="Upload a directory recursively to the device. Usage: -u SRC [DEST] (DEST is optional, defaults to basename of SRC)"
|
help="Upload a directory recursively to the device. Usage: -u SRC [DEST] (DEST is optional, defaults to basename of SRC)"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
"--src",
|
||||||
|
nargs="?",
|
||||||
|
const="src",
|
||||||
|
metavar="DIR",
|
||||||
|
help="Upload DIR recursively to device root (:/, no leading directory). If DIR is omitted, uses local ./src."
|
||||||
|
)
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
"--lib",
|
||||||
|
metavar="DIR",
|
||||||
|
help="Upload DIR recursively to /lib on device"
|
||||||
|
)
|
||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"-e",
|
"-e",
|
||||||
dest="erase_all",
|
dest="erase_all",
|
||||||
@@ -410,9 +466,12 @@ Examples:
|
|||||||
if ran_reset:
|
if ran_reset:
|
||||||
time.sleep(0.5)
|
time.sleep(0.5)
|
||||||
try:
|
try:
|
||||||
|
if value is None:
|
||||||
print(f"Following output from {port}... (Press Ctrl+C to stop)", file=sys.stderr)
|
print(f"Following output from {port}... (Press Ctrl+C to stop)", file=sys.stderr)
|
||||||
|
else:
|
||||||
|
print(f"Following output from {port} for {value} seconds...", file=sys.stderr)
|
||||||
conn = DeviceConnection(port)
|
conn = DeviceConnection(port)
|
||||||
conn.follow_output()
|
conn.follow_output(value)
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
print("\nStopped following.", file=sys.stderr)
|
print("\nStopped following.", file=sys.stderr)
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
@@ -421,10 +480,6 @@ Examples:
|
|||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
return # follow blocks; when interrupted we're done
|
return # follow blocks; when interrupted we're done
|
||||||
|
|
||||||
# If we ran any actions and follow wasn't last, we're done
|
|
||||||
if ordered_actions:
|
|
||||||
return
|
|
||||||
|
|
||||||
# Collect all edit parameters
|
# Collect all edit parameters
|
||||||
edits: Dict[str, Any] = {}
|
edits: Dict[str, Any] = {}
|
||||||
|
|
||||||
@@ -450,7 +505,11 @@ Examples:
|
|||||||
edits["pattern"] = args.preset
|
edits["pattern"] = args.preset
|
||||||
|
|
||||||
if args.default is not None:
|
if args.default is not None:
|
||||||
edits["startup_preset"] = args.default
|
edits["default"] = args.default
|
||||||
|
|
||||||
|
if args.device_id is not None:
|
||||||
|
# Clamp into single-byte range; store as int in settings.json
|
||||||
|
edits["id"] = max(0, min(255, args.device_id))
|
||||||
|
|
||||||
# 1. Download: get current settings from device
|
# 1. Download: get current settings from device
|
||||||
try:
|
try:
|
||||||
|
|||||||
13
device.py
13
device.py
@@ -208,12 +208,21 @@ class DeviceConnection:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise TransportError(f"Failed to reset device: {e}") from e
|
raise TransportError(f"Failed to reset device: {e}") from e
|
||||||
|
|
||||||
def follow_output(self):
|
def follow_output(self, duration=None):
|
||||||
"""Follow device output continuously (like tail -f)."""
|
"""
|
||||||
|
Follow device output (like tail -f).
|
||||||
|
|
||||||
|
Args:
|
||||||
|
duration: Optional number of seconds to follow output for. If None,
|
||||||
|
follow indefinitely until interrupted.
|
||||||
|
"""
|
||||||
# Use direct serial connection like dev.py does
|
# Use direct serial connection like dev.py does
|
||||||
|
start_time = time.time()
|
||||||
try:
|
try:
|
||||||
with serial.Serial(self.device, baudrate=115200) as ser:
|
with serial.Serial(self.device, baudrate=115200) as ser:
|
||||||
while True:
|
while True:
|
||||||
|
if duration is not None and (time.time() - start_time) >= duration:
|
||||||
|
break
|
||||||
if ser.in_waiting > 0:
|
if ser.in_waiting > 0:
|
||||||
data = ser.readline().decode('utf-8', errors='replace').strip()
|
data = ser.readline().decode('utf-8', errors='replace').strip()
|
||||||
if data:
|
if data:
|
||||||
|
|||||||
Reference in New Issue
Block a user