Compare commits
3 Commits
5f7acf38f0
...
713cd6e9a1
| Author | SHA1 | Date | |
|---|---|---|---|
| 713cd6e9a1 | |||
| 9e72c62481 | |||
|
|
eee9327e15 |
2
Pipfile
2
Pipfile
@@ -16,5 +16,5 @@ python_version = "3"
|
|||||||
[scripts]
|
[scripts]
|
||||||
web = "python web.py"
|
web = "python web.py"
|
||||||
cli = "python cli.py"
|
cli = "python cli.py"
|
||||||
build = "pyinstaller --clean led-cli.spec"
|
build = "pyinstaller --clean --onefile --name led-cli --paths lib cli.py"
|
||||||
install = "pipenv install"
|
install = "pipenv install"
|
||||||
|
|||||||
41
README.md
41
README.md
@@ -1,8 +1,39 @@
|
|||||||
# led-tool
|
# led-tool
|
||||||
- `-s, --show`: Display current settings from device
|
|
||||||
- `--no-download`: Don't download settings first (use empty settings)
|
|
||||||
**Default behavior:** Downloads settings and prints them. If any edit flags are provided, settings are modified and uploaded automatically (unless `--no-upload` is specified).
|
|
||||||
|
|
||||||
## Device Connection
|
CLI helpers for MicroPython LED devices: **`settings.json`** download/upload via **mpremote**, resets, follow/tail, recursive uploads, optional firmware flash, and **led-driver**-oriented flags (presets, transport, Wi-Fi fields).
|
||||||
|
|
||||||
The tools use an integrated mpremote transport to communicate with MicroPython devices. Make sure your device is connected and accessible via the specified serial port.## LicenseSee LICENSE file for details.
|
Connection is always via **`-p` / `--port`** (default `/dev/ttyACM0`). There is **no** separate “server IP” flag; the Pi or PC address is configured on the device in **`settings.json`** when using Wi-Fi mode.
|
||||||
|
|
||||||
|
## Common flags
|
||||||
|
|
||||||
|
| Flag | Purpose |
|
||||||
|
|------|--------|
|
||||||
|
| `-p`, `--port` | Serial device (default `/dev/ttyACM0`) |
|
||||||
|
| `-s`, `--show` | Print current settings from the device |
|
||||||
|
| `-n`, `--name` | Device **name** |
|
||||||
|
| `--id` | Numeric device id (ESP-NOW, 0–255) |
|
||||||
|
| `--pin` | LED GPIO (`led_pin`) |
|
||||||
|
| `-b`, `--brightness` | Brightness 0–255 |
|
||||||
|
| `-l`, `--leds` | LED count (`num_leds`) |
|
||||||
|
| `-d`, `--debug` | Debug 0 or 1 |
|
||||||
|
| `-o`, `--order` | LED colour order (`rgb`, `grb`, …) |
|
||||||
|
| `--preset` / `--pattern` | Create or replace a named preset in **led-driver** `presets.json` |
|
||||||
|
| `--default` | Startup preset name |
|
||||||
|
| `--transport` | `espnow` or `wifi` (`transport_type` on device) |
|
||||||
|
| `--ssid`, `--wifi-password`, `--wifi-channel` | Wi-Fi / channel fields for the driver |
|
||||||
|
| `-r`, `--reset` | Reset the device |
|
||||||
|
| `-f`, `--follow` | Follow serial output (optional timeout seconds) |
|
||||||
|
| `--pause` | Sleep N seconds (for chained actions) |
|
||||||
|
| `-u`, `--upload` | Recursive upload: `-u SRC [DEST]` |
|
||||||
|
| `--src`, `--lib` | Upload `src/` or a tree to `/lib` |
|
||||||
|
| `-e` | Erase device code (keeps `settings.json`) |
|
||||||
|
| `--rm` | Remove a path on the device |
|
||||||
|
| `--flash` | Flash a firmware binary (uses **esptool** on the host) |
|
||||||
|
|
||||||
|
**Default behaviour:** the tool always downloads `settings.json`, prints the merged view, and uploads again **only** when you pass setting edits (`--name`, `--leds`, …), **`--preset`**, or the relevant upload/flash/erase actions in order.
|
||||||
|
|
||||||
|
Run **`python cli.py -h`** for the full epilog and argument list.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
See **LICENSE** in this directory.
|
||||||
|
|||||||
37
cli.py
37
cli.py
@@ -154,7 +154,7 @@ _FLAGS_WITH_VALUE = frozenset({
|
|||||||
def _get_ordered_actions(argv: List[str]) -> List[tuple]:
|
def _get_ordered_actions(argv: List[str]) -> List[tuple]:
|
||||||
"""
|
"""
|
||||||
Scan argv and return list of (action_name, value) in order of appearance.
|
Scan argv and return list of (action_name, value) in order of appearance.
|
||||||
Actions: flash, pause, reset, upload, erase_all, rm, follow.
|
Actions: flash, pause, reset, upload, ls, erase_all, rm, follow.
|
||||||
"""
|
"""
|
||||||
actions = []
|
actions = []
|
||||||
i = 1
|
i = 1
|
||||||
@@ -205,13 +205,18 @@ def _get_ordered_actions(argv: List[str]) -> List[tuple]:
|
|||||||
actions.append(('upload', [local_dir, ""]))
|
actions.append(('upload', [local_dir, ""]))
|
||||||
continue
|
continue
|
||||||
if arg == '--lib':
|
if arg == '--lib':
|
||||||
# Upload local DIR to /lib on device
|
# Upload local DIR (default: ./lib) to /lib on device
|
||||||
if i + 1 < len(argv):
|
local_dir = "lib"
|
||||||
|
if i + 1 < len(argv) and not argv[i + 1].startswith('-'):
|
||||||
local_dir = argv[i + 1]
|
local_dir = argv[i + 1]
|
||||||
actions.append(('upload', [local_dir, "lib"]))
|
|
||||||
i += 2
|
i += 2
|
||||||
else:
|
else:
|
||||||
raise ValueError("--lib requires a directory argument")
|
i += 1
|
||||||
|
actions.append(('upload', [local_dir, "lib"]))
|
||||||
|
continue
|
||||||
|
if arg == '--ls':
|
||||||
|
actions.append(('ls', None))
|
||||||
|
i += 1
|
||||||
continue
|
continue
|
||||||
if arg == '-e':
|
if arg == '-e':
|
||||||
actions.append(('erase_all', None))
|
actions.append(('erase_all', None))
|
||||||
@@ -434,8 +439,16 @@ Examples:
|
|||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--lib",
|
"--lib",
|
||||||
|
nargs="?",
|
||||||
|
const="lib",
|
||||||
metavar="DIR",
|
metavar="DIR",
|
||||||
help="Upload DIR recursively to /lib on device"
|
help="Upload DIR recursively to /lib on device. If DIR is omitted, uses local ./lib."
|
||||||
|
)
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
"--ls",
|
||||||
|
action="store_true",
|
||||||
|
help="List files on the device root (:/)"
|
||||||
)
|
)
|
||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
@@ -527,6 +540,18 @@ Examples:
|
|||||||
print(f"Error uploading directory: {e}", file=sys.stderr)
|
print(f"Error uploading directory: {e}", file=sys.stderr)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
elif action_name == 'ls':
|
||||||
|
try:
|
||||||
|
print(f"Listing files on device {port}...", file=sys.stderr)
|
||||||
|
conn = DeviceConnection(port)
|
||||||
|
items = conn.list_files('')
|
||||||
|
for name, is_dir, size in items:
|
||||||
|
marker = "d" if is_dir else "-"
|
||||||
|
print(f"{marker} {size:>8} {name}")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error listing files: {e}", file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
elif action_name == 'erase_all':
|
elif action_name == 'erase_all':
|
||||||
try:
|
try:
|
||||||
print(f"Erasing all code on device {port}...", file=sys.stderr)
|
print(f"Erasing all code on device {port}...", file=sys.stderr)
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ pipenv install "$@"
|
|||||||
if [ ! -f "dist/led-cli" ] || [ "cli.py" -nt "dist/led-cli" ] || [ "device.py" -nt "dist/led-cli" ]; then
|
if [ ! -f "dist/led-cli" ] || [ "cli.py" -nt "dist/led-cli" ] || [ "device.py" -nt "dist/led-cli" ]; then
|
||||||
echo ""
|
echo ""
|
||||||
echo "Building binary..."
|
echo "Building binary..."
|
||||||
pipenv run pyinstaller --clean led-cli.spec
|
pipenv run pyinstaller --clean --onefile --name led-cli --paths lib cli.py
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Ensure ~/.local/bin exists
|
# Ensure ~/.local/bin exists
|
||||||
|
|||||||
Reference in New Issue
Block a user