chore: delete unreferenced scripts and archived docs; fix stale doc claims (#607)

* chore(scripts): delete unreferenced helper scripts

None of these is referenced by an installer, systemd unit, CI workflow,
test, the web UI or src/:

- utils/cleanup_venv.sh removes venv_web_v2, which nothing creates
- utils/clear_python_cache.sh hardcodes ~/LEDMatrix and a .webassets-cache
  nothing uses
- install/migrate_config.sh only copies the template, which the installer
  and ConfigManager already do
- install/debug_install.sh, debug/debug_web_manual.py
- diagnose_web_ui.sh and verify_web_ui.sh overlap diagnose_web_interface.sh,
  which the docs point to
- fix_internet_connectivity.sh is iptables-only (stale on nftables)
- diagnose_plugin_permissions.sh, dev/validate_python.py
- download_nba_logos.py + README_NBA_LOGOS.md: logo_downloader fetches
  logos on demand
- setup_plugin_repos.py linked into the production plugin-repos/ dir; the
  dev workflow is scripts/dev/dev_plugin_setup.sh, and
  MULTI_ROOT_WORKSPACE_SETUP.md now uses it

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(config): drop unused plugin_system flags and a dead unit comment

- config.template.json: remove plugin_system.auto_discover,
  auto_load_enabled and development_mode. Nothing reads them; the web UI
  only stores them when a client sends them. ConfigManager's migration
  only adds template keys, so existing configs keep theirs unchanged.
- config.template.json: re-indent vegas_scroll's live_* keys.
- systemd/ledmatrix.service: remove the comment documenting
  LEDMATRIX_ON_DEMAND_PLUGIN / on_demand_env.conf; nothing reads either.
- CONFIG_REFERENCE.md: say the legacy keys are no longer in the template.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs: delete docs/archive and PLUGIN_IMPLEMENTATION_SUMMARY.md

- docs/archive/: superseded guides; the repository history keeps them
  and no live doc links into the directory. The one open document in it,
  WEB_UI_AUDIT_2026-09.md, moves to docs/audits/ and is linked from the
  docs index.
- PLUGIN_IMPLEMENTATION_SUMMARY.md invented usage statistics, called
  v2.0.0 current, listed shipped auto-updates as future work and
  documented a BasePlugin.get_config() that does not exist.
- docs/README.md: drop both, and stop telling contributors to archive
  obsolete pages instead of deleting them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(plugin-api): fix extra_small_font size, cache metric key and scroll pacing example

- PLUGIN_API_REFERENCE: extra_small_font loads at 7, not 6 (crisp_size
  snaps it, src/display_manager.py); get_cache_metrics() returns
  cache_hit_rate, not hit_rate (src/cache/cache_metrics.py).
- ADVANCED_PLUGIN_DEVELOPMENT: the basic scrolling example slept in a loop
  and never passed frame_hold; use ScrollHelper + scroll_config.configure()
  and set_scrolling_state(True, frame_hold=...) as PLUGIN_API_REFERENCE does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(plugin-config): match the config tab, icon and web-action docs to the code

- PLUGIN_CONFIG_QUICK_START / PLUGIN_CONFIGURATION_TABS /
  PLUGIN_CONFIGURATION_GUIDE: there is no "Reset to Defaults" button (the
  tab has Refresh, Update, Uninstall, Save Configuration); plugin config
  hot-reloads (ConfigService + on_config_change), so no restart; the
  schema is found by the fixed name config_schema.json, not a manifest
  config_schema field; the tab row is "Plugin Manager", not "Plugins";
  forms are server-rendered from /v3/partials/plugin-config/<id>; the
  duration hook is get_display_duration()/display_duration; a class_name
  mismatch raises PluginError; the store requires id, name, class_name and
  display_modes (not version); plugin_system.debug/log_level do not exist
  (use run.py -d / LEDMATRIX_DEBUG). Drop "future" features that shipped.
- PLUGIN_CONFIG_CORE_PROPERTIES: list all of CORE_PLUGIN_PROPERTIES,
  including skin, skin_options and the vegas_* tuning keys.
- PLUGIN_CUSTOM_ICONS: icon is only a Font Awesome class (fallback
  fa-puzzle-piece); emoji/URL icons and getPluginIcon() never existed in
  v3. Note that /api/v3/plugins/installed currently omits icon.
- PLUGIN_WEB_UI_ACTIONS (+ example JSON): success_message, error_message
  and step1_message are never read.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(store): describe the monorepo registry and the store UI as they are

- PLUGIN_STORE_GUIDE: the Plugin Store is a section of the Plugin
  Manager tab; URL installs are "Install from GitHub" -> "Install Single
  Plugin"; bulk update exists (Check & Update All) plus opt-in weekly
  auto-update; PluginStoreManager() defaults to plugins/, so the Python
  examples pass plugin-repos; registry plugins are downloaded (GitHub API,
  ZIP fallback), not cloned; updates compare version with latest_version.
- PLUGIN_REGISTRY_SETUP_GUIDE: replace the per-plugin-repo + tag
  walkthrough with a short page on the monorepo registry (plugin_path,
  latest_version, update_registry.py) that points at the monorepo's own
  SUBMISSION.md. Drops the reference to the deleted
  PLUGIN_IMPLEMENTATION_SUMMARY.md and setup_plugin_repos.py.
- plugin_registry_template.json: use the real entry shape.
- PLUGIN_QUICK_REFERENCE: automatic background updates exist (opt-in);
  registry example and publishing steps use the monorepo, not tags.
- PLUGIN_DEVELOPMENT_GUIDE: tags/releases are not read by the store.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(readme): fix the Triple Bonnet mapping, install prerequisites and backup names

- README: the Adafruit Triple Bonnet uses `regular` (3 outputs), not
  `regular-pi1` (1 output) -- src/matrix_support.py MAPPING_OUTPUTS, and
  the README's own hardware_mapping section; the template default mapping
  is adafruit-hat, the PWM mod switches it to adafruit-hat-pwm; manual
  install only needs git up front (first_time_install.sh installs
  python-dev-is-python3, cmake, ninja-build etc.; cython3/scons are not
  used); the Pi Zero 2 W is a supported low-memory board, consistent with
  PRODUCT.md, LOW_MEMORY_BOARDS.md and the installer's low-memory build;
  fix the "First_time_install.sh" spelling, an orphan "2." list item and
  the hello-world starter link (it lives in the plugins monorepo).
- CONFIG_DEBUGGING: automatic backups are
  config/backups/config.json.backup.<YYYYMMDD_HHMMSS_ffffff> (five kept),
  not config_YYYYMMDD_HHMMSS.json.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(dev): correct the test-running and rgbmatrix build instructions

- HOW_TO_RUN_TESTS: coverage is not collected by a plain pytest run and
  pytest.ini has no threshold; the only one is --cov-fail-under=52 in the
  core unit-test job of .github/workflows/test.yml, which runs the whole
  test/ tree (not an allowlist). Almost no tests carry markers, so
  -m integration / -m slow select nothing; drop them and -m unit as the
  quick check. Replace the hardcoded /home/chuck path.
- DEVELOPMENT: the rgbmatrix package is built with pip install . from
  the submodule root (scikit-build-core + CMake + Ninja), as
  first_time_install.sh does; there is no make build-python /
  bindings/python step, and the build deps are python-dev-is-python3,
  cmake and ninja-build, not cython3/scons.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(wifi): the setup AP is open; auto-enable can be turned off without code changes

- WIFI_NETWORK_SETUP / SSH_UNAVAILABLE_AFTER_INSTALL: both AP paths in
  src/wifi_manager.py create an open network and nothing reads
  ap_password, so drop the "ledmatrix123" password and the ap_password
  key/advice.
- SSH_UNAVAILABLE_AFTER_INSTALL: disabling automatic AP mode does not
  need code changes -- auto_enable_ap_mode is a WiFi-tab toggle and
  POST /api/v3/wifi/ap/auto-enable; note the monitor daemon reads
  wifi_config.json at start, so restart it after changing the setting.
  Use the ledpi username and a relative install path like the other docs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(reference): add auto_update, drop drifted line numbers, fix UI and service details

- CONFIG_REFERENCE: document the top-level auto_update.enabled key (read
  by web_interface/auto_update.py and src/auto_update_setup.py); replace
  drifted file:line references with function names; the template's
  dim_schedule mode is "global".
- ADVANCED_FEATURES: core does not read a per-plugin background_service
  block (the sports plugins read their own), and priority is "higher
  number = higher priority" on FetchRequest but not used for ordering.
- WEB_INTERFACE_GUIDE: the General tab toggle is "Web Display Autostart"
  (web interface service), brightness is 1-100, and config paths are
  relative to the LEDMatrix folder, not /config.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs: drop references to code removed in #608

get_installed_plugin_info, WiFiManager's saved_networks and the six
always-skipping plugin test files are deleted there. NetworkManager already
remembers joined networks; LEDMatrix no longer stores WiFi passwords.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs: don't link SKIN_SYSTEM.md from the core-properties page

#615 deletes SKIN_SYSTEM.md; with this link, whichever of the two merged
second would break test_doc_links. The skin/skin_options entries go when
#615 removes the keys.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Chuck
2026-09-23 12:36:38 -04:00
committed by GitHub
co-authored by Claude Opus 5.5
parent 84afa9d64f
commit a231d4dbc7
87 changed files with 540 additions and 13856 deletions
-120
View File
@@ -1,120 +0,0 @@
# NBA Logo Downloader
This script downloads all NBA team logos from the ESPN API and saves
them in the `assets/sports/nba_logos/` directory.
> **Heads up:** the NBA leaderboard and basketball scoreboards now
> live as plugins in the
> [`ledmatrix-plugins`](https://github.com/ChuckBuilds/ledmatrix-plugins)
> repo (`basketball-scoreboard`, `ledmatrix-leaderboard`). Those
> plugins download the logos they need automatically on first display.
> This standalone script is mainly useful when you want to pre-populate
> the assets directory ahead of time, or for development/debugging.
All commands below should be run from the LEDMatrix project root.
## Usage
### Basic Usage
```bash
python3 scripts/download_nba_logos.py
```
### Force Re-download
If you want to re-download all logos (even if they already exist):
```bash
python3 scripts/download_nba_logos.py --force
```
### Quiet Mode
Reduce logging output:
```bash
python3 scripts/download_nba_logos.py --quiet
```
### Combined Options
```bash
python3 scripts/download_nba_logos.py --force --quiet
```
## What It Does
1. **Fetches NBA Team Data**: Gets the complete list of NBA teams from ESPN API
2. **Downloads Logos**: Downloads each team's logo from ESPN's servers
3. **Saves Locally**: Saves logos as `{team_abbr}.png` in `assets/sports/nba_logos/`
4. **Skips Existing**: By default, skips teams that already have logos
5. **Rate Limiting**: Includes small delays between downloads to be respectful to the API
## Expected Output
```
🏀 Starting NBA logo download...
Target directory: assets/sports/nba_logos/
Force download: False
✅ NBA logo download complete!
📊 Summary: 30 downloaded, 0 failed
🎉 NBA logos are now ready for use in the leaderboard!
```
## File Structure
After running the script, you'll have:
```
assets/sports/nba_logos/
├── ATL.png # Atlanta Hawks
├── BOS.png # Boston Celtics
├── BKN.png # Brooklyn Nets
├── CHA.png # Charlotte Hornets
├── CHI.png # Chicago Bulls
├── CLE.png # Cleveland Cavaliers
├── DAL.png # Dallas Mavericks
├── DEN.png # Denver Nuggets
├── DET.png # Detroit Pistons
├── GSW.png # Golden State Warriors
├── HOU.png # Houston Rockets
├── IND.png # Indiana Pacers
├── LAC.png # LA Clippers
├── LAL.png # Los Angeles Lakers
├── MEM.png # Memphis Grizzlies
├── MIA.png # Miami Heat
├── MIL.png # Milwaukee Bucks
├── MIN.png # Minnesota Timberwolves
├── NOP.png # New Orleans Pelicans
├── NYK.png # New York Knicks
├── OKC.png # Oklahoma City Thunder
├── ORL.png # Orlando Magic
├── PHI.png # Philadelphia 76ers
├── PHX.png # Phoenix Suns
├── POR.png # Portland Trail Blazers
├── SAC.png # Sacramento Kings
├── SAS.png # San Antonio Spurs
├── TOR.png # Toronto Raptors
├── UTA.png # Utah Jazz
└── WAS.png # Washington Wizards
```
## Integration with NBA plugins
Once the logos are in `assets/sports/nba_logos/`, both the
`basketball-scoreboard` and `ledmatrix-leaderboard` plugins will pick
them up automatically and skip their own first-run download. This is
useful if you want to deploy a Pi without internet access to ESPN, or
if you want to preview the display on your dev machine without
waiting for downloads.
## Troubleshooting
### "Import error: No module named 'requests'"
Make sure you're running this from the LEDMatrix project directory where all dependencies are installed.
### "Permission denied" errors
Make sure the script has write permissions to the `assets/sports/nba_logos/` directory.
### Some logos fail to download
This is normal - some teams might have temporary API issues or the ESPN API might be rate-limiting. The script will continue with the successful downloads.
## Requirements
- Python 3.9+ (matches the project's overall minimum)
- `requests` library (already in `requirements.txt`)
- Write access to `assets/sports/nba_logos/` directory
-96
View File
@@ -1,96 +0,0 @@
#!/usr/bin/env python3
"""
Web Interface Manual Debug Script
Run this to diagnose why web_interface/start.py isn't working
"""
import sys
import os
import traceback
from pathlib import Path
def main():
print("🔍 LED Matrix Web Interface Debug Tool")
print("=" * 50)
# Change to project root (two levels up from scripts/debug/)
project_root = Path(__file__).parent.parent.parent.resolve()
os.chdir(project_root)
print(f"📁 Working directory: {os.getcwd()}")
# Add to Python path
sys.path.insert(0, str(project_root))
print(f"🔗 Python path includes: {project_root}")
print("\n1. Testing basic imports...")
try:
import flask
print(f" ✅ Flask: {flask.__version__}")
except ImportError as e:
print(f" ❌ Flask missing: {e}")
return False
try:
from src.config_manager import ConfigManager
print(" ✅ ConfigManager imported")
except Exception as e:
print(f" ❌ ConfigManager failed: {e}")
traceback.print_exc()
return False
print("\n2. Testing web interface imports...")
try:
from web_interface.app import app
print(" ✅ web_interface.app imported")
print(f" 📋 App object: {app}")
except Exception as e:
print(f" ❌ web_interface.app failed: {e}")
traceback.print_exc()
return False
print("\n3. Checking config...")
try:
config_manager = ConfigManager()
config = config_manager.load_config()
print(" ✅ Config loaded")
# Same rule ledmatrix-web.service applies: only an explicit false/off
# keeps the web interface down; a missing key means on.
sys.path.insert(0, str(project_root / 'scripts' / 'utils'))
from start_web_conditionally import autostart_enabled
raw = config.get('web_display_autostart', '(not set, defaults to on)')
state = 'starts' if autostart_enabled(config) else 'will NOT start'
print(f" 🔧 web_display_autostart: {raw} (web interface {state})")
except Exception as e:
print(f" ❌ Config check failed: {e}")
traceback.print_exc()
return False
print("\n4. Testing Flask startup...")
try:
print(" 🚀 Starting Flask app...")
print(" 📍 Will run on: http://0.0.0.0:5000")
print(" ⏹️ Press Ctrl+C to stop")
# Run the app (debug mode controlled by env var to satisfy security scanners)
_debug = os.environ.get('LEDMATRIX_FLASK_DEBUG', '0') == '1'
app.run(host='0.0.0.0', port=5000, debug=_debug)
except KeyboardInterrupt:
print("\n ⏹️ Server stopped by user")
return True
except Exception as e:
print(f" ❌ Flask startup failed: {e}")
traceback.print_exc()
return False
if __name__ == "__main__":
try:
success = main()
if success:
print("\n✅ Debug completed successfully")
else:
print("\n❌ Debug found issues - check output above")
except Exception as e:
print(f"\n💥 Debug script crashed: {e}")
traceback.print_exc()
-6
View File
@@ -6,7 +6,6 @@ This directory contains scripts and utilities for development and testing.
- **`dev_plugin_setup.sh`** - Sets up plugin development environment by linking plugin repositories
- **`run_emulator.sh`** - Runs the LED Matrix display in emulator mode (for development without hardware)
- **`validate_python.py`** - Validates Python files for common formatting and syntax errors
## Usage
@@ -30,8 +29,3 @@ links. To use a fork or another clone location, copy
./scripts/dev/run_emulator.sh
```
### Validating Python Files
```bash
python3 scripts/dev/validate_python.py <file.py>
```
-95
View File
@@ -1,95 +0,0 @@
#!/usr/bin/env python3
"""
Python file validation script to prevent common formatting errors.
This script checks for:
1. Proper indentation (4 spaces, no mixed tabs/spaces)
2. Missing imports
3. Syntax errors
4. Line length issues
5. Proper try/except structure
Usage: python tools/validate_python.py <python_file>
"""
import ast
import sys
import os
def validate_file(filepath: str) -> bool:
"""Validate a Python file for common issues."""
try:
with open(filepath, 'r', encoding='utf-8') as f:
content = f.read()
issues_found = []
# Check for tabs (should use spaces)
if '\t' in content:
issues_found.append("❌ Contains tabs - use 4 spaces instead")
# Check for trailing whitespace
lines = content.split('\n')
for i, line in enumerate(lines, 1):
if line.rstrip() != line:
issues_found.append(f"❌ Line {i}: Trailing whitespace")
# Check for very long lines
for i, line in enumerate(lines, 1):
if len(line) > 120:
issues_found.append(f"⚠️ Line {i}: Very long line ({len(line)} chars)")
# Check for proper try/except structure
try:
ast.parse(content)
except SyntaxError as e:
issues_found.append(f"❌ Syntax error: {e}")
# Check for mixed quotes (inconsistency)
single_quotes = content.count("'")
double_quotes = content.count('"')
if single_quotes > 0 and double_quotes > 0:
issues_found.append("⚠️ Mixed quote usage - consider using double quotes consistently")
# Report results
if issues_found:
print(f"\n🔍 Validation Results for: {filepath}")
print("=" * 50)
for issue in issues_found:
print(issue)
return False
else:
print(f"✅ {filepath} - All checks passed!")
return True
except Exception as e:
print(f"❌ Error reading file {filepath}: {e}")
return False
def validate_directory(directory: str) -> bool:
"""Validate all Python files in a directory."""
all_passed = True
for root, dirs, files in os.walk(directory):
for file in files:
if file.endswith('.py'):
filepath = os.path.join(root, file)
if not validate_file(filepath):
all_passed = False
return all_passed
if __name__ == "__main__":
if len(sys.argv) != 2:
print("Usage: python tools/validate_python.py <file_or_directory>")
sys.exit(1)
target = sys.argv[1]
if os.path.isfile(target):
validate_file(target)
elif os.path.isdir(target):
validate_directory(target)
else:
print(f"❌ Path not found: {target}")
sys.exit(1)
-171
View File
@@ -1,171 +0,0 @@
#!/bin/bash
# Diagnostic script for plugin directory permissions
# Run this on the Raspberry Pi to check and fix plugin directory permissions
set -e
echo "=========================================="
echo "Plugin Directory Permissions Diagnostic"
echo "=========================================="
echo ""
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# Get the actual user
if [ -n "$SUDO_USER" ]; then
ACTUAL_USER="$SUDO_USER"
else
ACTUAL_USER=$(whoami)
fi
# Get project root (parent of scripts/ directory)
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
PROJECT_ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
PLUGINS_DIR="$PROJECT_ROOT_DIR/plugins"
PLUGIN_REPOS_DIR="$PROJECT_ROOT_DIR/plugin-repos"
echo "Project root: $PROJECT_ROOT_DIR"
echo "User: $ACTUAL_USER"
echo "Running as: $(whoami)"
echo ""
# Check parent directory permissions
echo "=== Parent Directory Permissions ==="
echo "Checking /home/$ACTUAL_USER:"
if [ -d "/home/$ACTUAL_USER" ]; then
PERMS=$(stat -c "%a %U:%G" "/home/$ACTUAL_USER")
echo " Permissions: $PERMS"
if [ "$(stat -c "%a" "/home/$ACTUAL_USER")" != "755" ] && [ "$(stat -c "%a" "/home/$ACTUAL_USER")" != "750" ]; then
echo -e " ${YELLOW}⚠ Warning: Home directory has restrictive permissions${NC}"
echo " Root may not be able to access subdirectories"
fi
else
echo -e " ${RED}✗ Home directory not found${NC}"
fi
echo ""
echo "Checking $PROJECT_ROOT_DIR:"
if [ -d "$PROJECT_ROOT_DIR" ]; then
PERMS=$(stat -c "%a %U:%G" "$PROJECT_ROOT_DIR")
echo " Permissions: $PERMS"
if [ "$(stat -c "%a" "$PROJECT_ROOT_DIR")" != "755" ] && [ "$(stat -c "%a" "$PROJECT_ROOT_DIR")" != "750" ]; then
echo -e " ${YELLOW}⚠ Warning: Project directory has restrictive permissions${NC}"
fi
else
echo -e " ${RED}✗ Project directory not found${NC}"
fi
echo ""
# Check plugins directory
echo "=== Plugins Directory ==="
if [ -d "$PLUGINS_DIR" ]; then
PERMS=$(stat -c "%a %U:%G" "$PLUGINS_DIR")
echo " Path: $PLUGINS_DIR"
echo " Permissions: $PERMS"
OWNER=$(stat -c "%U" "$PLUGINS_DIR")
GROUP=$(stat -c "%G" "$PLUGINS_DIR")
PERM_BITS=$(stat -c "%a" "$PLUGINS_DIR")
if [ "$OWNER" = "root" ] && [ "$GROUP" = "$ACTUAL_USER" ] && [ "$PERM_BITS" = "775" ]; then
echo -e " ${GREEN}✓ Correct permissions${NC}"
else
echo -e " ${RED}✗ Incorrect permissions${NC}"
echo " Expected: root:$ACTUAL_USER 775"
echo " Actual: $OWNER:$GROUP $PERM_BITS"
fi
# Check if root can access
if sudo -u root test -r "$PLUGINS_DIR" && sudo -u root test -w "$PLUGINS_DIR"; then
echo -e " ${GREEN}✓ Root can read/write${NC}"
else
echo -e " ${RED}✗ Root cannot access${NC}"
fi
else
echo -e " ${YELLOW}⚠ Directory does not exist${NC}"
fi
echo ""
# Check plugin-repos directory
echo "=== Plugin-Repos Directory ==="
if [ -d "$PLUGIN_REPOS_DIR" ]; then
PERMS=$(stat -c "%a %U:%G" "$PLUGIN_REPOS_DIR")
echo " Path: $PLUGIN_REPOS_DIR"
echo " Permissions: $PERMS"
OWNER=$(stat -c "%U" "$PLUGIN_REPOS_DIR")
GROUP=$(stat -c "%G" "$PLUGIN_REPOS_DIR")
PERM_BITS=$(stat -c "%a" "$PLUGIN_REPOS_DIR")
if [ "$OWNER" = "root" ] && [ "$GROUP" = "$ACTUAL_USER" ] && [ "$PERM_BITS" = "775" ]; then
echo -e " ${GREEN}✓ Correct permissions${NC}"
else
echo -e " ${RED}✗ Incorrect permissions${NC}"
echo " Expected: root:$ACTUAL_USER 775"
echo " Actual: $OWNER:$GROUP $PERM_BITS"
fi
# Check if root can access
if sudo -u root test -r "$PLUGIN_REPOS_DIR" && sudo -u root test -w "$PLUGIN_REPOS_DIR"; then
echo -e " ${GREEN}✓ Root can read/write${NC}"
else
echo -e " ${RED}✗ Root cannot access${NC}"
fi
# Try to list contents as root
echo " Testing root access:"
if sudo -u root ls "$PLUGIN_REPOS_DIR" >/dev/null 2>&1; then
echo -e " ${GREEN}✓ Root can list directory${NC}"
else
echo -e " ${RED}✗ Root cannot list directory${NC}"
echo " Error: $(sudo -u root ls "$PLUGIN_REPOS_DIR" 2>&1 | head -1)"
fi
else
echo -e " ${YELLOW}⚠ Directory does not exist${NC}"
fi
echo ""
# Test if root can create files
echo "=== Testing Root Write Access ==="
TEST_FILE="$PLUGIN_REPOS_DIR/.permission_test_$$"
if sudo -u root touch "$TEST_FILE" 2>/dev/null; then
echo -e " ${GREEN}✓ Root can create files${NC}"
sudo -u root rm -f "$TEST_FILE"
else
echo -e " ${RED}✗ Root cannot create files${NC}"
echo " Error: $(sudo -u root touch "$TEST_FILE" 2>&1)"
fi
echo ""
# Summary and fix recommendations
echo "=== Summary ==="
NEEDS_FIX=false
if [ ! -d "$PLUGIN_REPOS_DIR" ]; then
echo -e "${YELLOW}⚠ plugin-repos directory does not exist${NC}"
NEEDS_FIX=true
elif [ "$(stat -c "%U:%G" "$PLUGIN_REPOS_DIR" 2>/dev/null)" != "root:$ACTUAL_USER" ] || [ "$(stat -c "%a" "$PLUGIN_REPOS_DIR" 2>/dev/null)" != "775" ]; then
echo -e "${RED}✗ plugin-repos has incorrect permissions${NC}"
NEEDS_FIX=true
fi
if [ "$NEEDS_FIX" = true ]; then
echo ""
echo "=== Fix Commands ==="
echo "Run these commands to fix permissions:"
echo ""
echo "sudo mkdir -p $PLUGIN_REPOS_DIR"
echo "sudo chown root:$ACTUAL_USER $PLUGIN_REPOS_DIR"
echo "sudo chmod 775 $PLUGIN_REPOS_DIR"
echo ""
echo "Or run the fix script:"
echo "sudo bash $PROJECT_ROOT_DIR/scripts/fix_perms/fix_plugin_permissions.sh"
else
echo -e "${GREEN}✓ All permissions look correct${NC}"
fi
echo ""
-232
View File
@@ -1,232 +0,0 @@
#!/bin/bash
# LEDMatrix Web UI Diagnostic Script
# Run this on your Raspberry Pi to diagnose web UI startup issues
set -e
echo "=========================================="
echo "LEDMatrix Web UI Diagnostic Report"
echo "=========================================="
echo ""
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# Check if running as root or with sudo
if [ "$EUID" -ne 0 ]; then
echo -e "${YELLOW}Warning: Some checks require sudo. Running what we can...${NC}"
fi
PROJECT_DIR="${HOME}/LEDMatrix"
# Report web_display_autostart the way scripts/utils/start_web_conditionally.py
# (what ledmatrix-web.service runs) decides it: only an explicit false/off keeps
# the web interface down; a missing key or an unreadable config starts it.
# Prints "on <value>", "off <value>", "default" (key not set) or "unreadable".
web_autostart_state() {
(cd "$1" && python3 - 2>/dev/null <<'PY'
import json, os, sys
sys.path.insert(0, os.path.join(os.getcwd(), "scripts", "utils"))
try:
from start_web_conditionally import autostart_enabled
except Exception:
def autostart_enabled(config):
value = config.get("web_display_autostart", True)
if isinstance(value, str):
return value.strip().lower() not in ("off", "false", "no", "0")
return bool(value)
try:
with open(os.path.join("config", "config.json"), encoding="utf-8") as f:
config = json.load(f)
except Exception:
config = None
if not isinstance(config, dict):
print("unreadable")
elif "web_display_autostart" not in config:
print("default")
else:
raw = json.dumps(config["web_display_autostart"])
print(("on " if autostart_enabled(config) else "off ") + raw)
PY
) || echo "unknown"
}
echo "1. Checking service status..."
echo "------------------------------"
if systemctl is-active --quiet ledmatrix-web 2>/dev/null || sudo systemctl is-active --quiet ledmatrix-web 2>/dev/null; then
echo -e "${GREEN}✓ Service is ACTIVE${NC}"
STATUS=$(sudo systemctl status ledmatrix-web --no-pager -l | head -n 3)
echo "$STATUS"
else
echo -e "${RED}✗ Service is NOT running${NC}"
sudo systemctl status ledmatrix-web --no-pager -l | head -n 10 || echo "Service may not be installed"
fi
echo ""
echo "2. Checking if service is enabled..."
echo "------------------------------"
if systemctl is-enabled --quiet ledmatrix-web 2>/dev/null || sudo systemctl is-enabled --quiet ledmatrix-web 2>/dev/null; then
echo -e "${GREEN}✓ Service is enabled to start on boot${NC}"
else
echo -e "${YELLOW}⚠ Service is NOT enabled (won't start on boot)${NC}"
fi
echo ""
echo "3. Checking configuration file..."
echo "------------------------------"
if [ -f "${PROJECT_DIR}/config/config.json" ]; then
echo -e "${GREEN}✓ Config file exists${NC}"
AUTOSTART=$(web_autostart_state "$PROJECT_DIR")
case "$AUTOSTART" in
on\ *)
echo -e "${GREEN}✓ web_display_autostart is ${AUTOSTART#on } (web UI starts)${NC}"
;;
off\ *)
echo -e "${RED}✗ web_display_autostart is ${AUTOSTART#off } (web UI won't start!)${NC}"
echo " Fix: Edit config.json and set 'web_display_autostart': true"
;;
default)
echo -e "${GREEN}✓ web_display_autostart is not set (defaults to on; web UI starts)${NC}"
;;
unreadable)
echo -e "${YELLOW}⚠ config.json could not be parsed (the web UI still starts so it can be repaired)${NC}"
;;
*)
echo -e "${YELLOW}⚠ Could not evaluate web_display_autostart (python3 unavailable?)${NC}"
;;
esac
else
echo -e "${RED}✗ Config file NOT FOUND at ${PROJECT_DIR}/config/config.json${NC}"
fi
echo ""
echo "4. Checking recent service logs..."
echo "------------------------------"
RECENT_LOGS=$(sudo journalctl -u ledmatrix-web -n 30 --no-pager 2>/dev/null || echo "No logs available")
if [ -n "$RECENT_LOGS" ] && [ "$RECENT_LOGS" != "No logs available" ]; then
echo "$RECENT_LOGS"
echo ""
echo "Key messages from logs:"
echo "$RECENT_LOGS" | grep -i "web_display_autostart\|Configuration\|Launching\|will not\|Failed\|Error\|Starting" || echo " (no key messages found)"
else
echo -e "${YELLOW}⚠ No recent logs found${NC}"
fi
echo ""
echo "5. Checking web interface files..."
echo "------------------------------"
FILES_TO_CHECK=(
"scripts/utils/start_web_conditionally.py"
"web_interface/start.py"
"web_interface/app.py"
"web_interface/requirements.txt"
"web_interface/blueprints/api_v3/__init__.py"
"web_interface/blueprints/pages_v3.py"
)
for file in "${FILES_TO_CHECK[@]}"; do
if [ -f "${PROJECT_DIR}/${file}" ]; then
echo -e "${GREEN}✓ ${file}${NC}"
else
echo -e "${RED}✗ ${file} MISSING${NC}"
fi
done
echo ""
echo "6. Checking Python import test..."
echo "------------------------------"
cd "${PROJECT_DIR}" 2>/dev/null || {
echo -e "${RED}✗ Cannot access project directory: ${PROJECT_DIR}${NC}"
echo ""
exit 1
}
if python3 -c "from web_interface.app import app; print('OK')" 2>&1; then
echo -e "${GREEN}✓ Flask app imports successfully${NC}"
else
echo -e "${RED}✗ Flask app import FAILED${NC}"
echo " Error details:"
python3 -c "from web_interface.app import app; print('OK')" 2>&1 | head -n 10
fi
echo ""
echo "7. Checking port 5000 availability..."
echo "------------------------------"
if command -v lsof &> /dev/null; then
PORT_CHECK=$(sudo lsof -i :5000 2>/dev/null || echo "")
if [ -z "$PORT_CHECK" ]; then
echo -e "${GREEN}✓ Port 5000 is available${NC}"
else
echo -e "${YELLOW}⚠ Port 5000 is in use:${NC}"
echo "$PORT_CHECK"
fi
elif command -v ss &> /dev/null; then
PORT_CHECK=$(sudo ss -tlnp | grep :5000 || echo "")
if [ -z "$PORT_CHECK" ]; then
echo -e "${GREEN}✓ Port 5000 is available${NC}"
else
echo -e "${YELLOW}⚠ Port 5000 is in use:${NC}"
echo "$PORT_CHECK"
fi
else
echo -e "${YELLOW}⚠ Cannot check port (lsof/ss not available)${NC}"
fi
echo ""
echo "8. Checking service file..."
echo "------------------------------"
if [ -f "/etc/systemd/system/ledmatrix-web.service" ]; then
echo -e "${GREEN}✓ Service file exists${NC}"
echo " Location: /etc/systemd/system/ledmatrix-web.service"
echo " WorkingDirectory: $(grep WorkingDirectory /etc/systemd/system/ledmatrix-web.service | cut -d'=' -f2 || echo 'not found')"
echo " ExecStart: $(grep ExecStart /etc/systemd/system/ledmatrix-web.service | cut -d'=' -f2 || echo 'not found')"
else
echo -e "${RED}✗ Service file NOT FOUND${NC}"
echo " Run: sudo ./install_web_service.sh (if available)"
fi
echo ""
echo "9. Checking dependencies..."
echo "------------------------------"
if [ -f "${PROJECT_DIR}/web_interface/requirements.txt" ]; then
echo "Checking if Flask is installed..."
if python3 -c "import flask; print(f'Flask {flask.__version__}')" 2>/dev/null; then
echo -e "${GREEN}✓ Flask is installed${NC}"
else
echo -e "${RED}✗ Flask is NOT installed${NC}"
echo " Run: pip3 install --break-system-packages -r web_interface/requirements.txt"
fi
else
echo -e "${YELLOW}⚠ requirements.txt not found${NC}"
fi
echo ""
echo "10. Manual startup test (dry run)..."
echo "------------------------------"
echo "To test manual startup, run:"
echo " cd ${PROJECT_DIR}"
echo " python3 web_interface/start.py"
echo ""
echo "=========================================="
echo "Diagnostic Summary"
echo "=========================================="
echo ""
echo "Most common issues:"
echo " 1. web_display_autostart is set to false in config.json (a missing key means on)"
echo " 2. Service not enabled or not started"
echo " 3. Missing dependencies (Flask, etc.)"
echo " 4. Import errors in web_interface/app.py"
echo " 5. Port 5000 already in use"
echo ""
echo "Next steps:"
echo " - Review the checks above for any RED ✗ marks"
echo " - Check recent logs: sudo journalctl -u ledmatrix-web -n 50 --no-pager"
echo " - Follow logs in real-time: sudo journalctl -u ledmatrix-web -f"
echo " - Try manual start: cd ${PROJECT_DIR} && python3 web_interface/start.py"
echo ""
echo "=========================================="
-98
View File
@@ -1,98 +0,0 @@
#!/usr/bin/env python3
"""
Script to download all NBA team logos from ESPN API and save them in assets/sports/nba_logos/
"""
import sys
import os
import logging
from typing import Tuple
# Add the project root to Python path so we can import the logo downloader
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
# Set up logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)
def download_nba_logos(force_download: bool = False) -> Tuple[int, int]:
"""
Download all NBA team logos from ESPN API.
Args:
force_download: Whether to re-download existing logos
Returns:
Tuple of (downloaded_count, failed_count)
"""
try:
from src.logo_downloader import download_all_logos_for_league
logger.info("🏀 Starting NBA logo download...")
logger.info(f"Target directory: assets/sports/nba_logos/")
logger.info(f"Force download: {force_download}")
# Use the existing function to download all NBA logos
downloaded_count, failed_count = download_all_logos_for_league('nba', force_download)
logger.info("✅ NBA logo download complete!")
logger.info(f"📊 Summary: {downloaded_count} downloaded, {failed_count} failed")
if downloaded_count > 0:
logger.info("🎉 NBA logos are now ready for use in the leaderboard!")
else:
logger.info("ℹ️ All NBA logos were already present or download failed for all teams")
return downloaded_count, failed_count
except ImportError as e:
logger.error(f"❌ Import error: {e}")
logger.info("💡 Make sure you're running this from the LEDMatrix project directory")
return 0, 0
except Exception as e:
logger.error(f"❌ Unexpected error: {e}")
return 0, 0
def main():
"""Main function with command line argument parsing."""
import argparse
parser = argparse.ArgumentParser(description='Download all NBA team logos from ESPN API')
parser.add_argument(
'--force',
action='store_true',
help='Force re-download of existing logos'
)
parser.add_argument(
'--quiet',
action='store_true',
help='Reduce logging output'
)
args = parser.parse_args()
# Set logging level based on quiet flag
if args.quiet:
logging.getLogger().setLevel(logging.WARNING)
logger.info("🚀 NBA Logo Downloader")
logger.info("=" * 50)
# Download the logos
downloaded, failed = download_nba_logos(args.force)
# Exit with appropriate code
if failed > 0 and downloaded == 0:
logger.error("❌ All downloads failed!")
sys.exit(1)
elif failed > 0:
logger.warning(f"⚠️ {failed} downloads failed, but {downloaded} succeeded")
sys.exit(0) # Partial success is still success
else:
logger.info("🎉 All NBA logos downloaded successfully!")
sys.exit(0)
if __name__ == "__main__":
main()
-109
View File
@@ -1,109 +0,0 @@
#!/bin/bash
# Emergency script to fix internet connectivity issues
# Run this if the Pi can't access the internet after AP mode testing
echo "=========================================="
echo "Internet Connectivity Fix Script"
echo "=========================================="
echo ""
# 1. Disable IP forwarding
echo "1. Disabling IP forwarding..."
sudo sysctl -w net.ipv4.ip_forward=0
echo " ✓ IP forwarding disabled"
# 2. Stop and disable dnsmasq (if it's interfering)
echo ""
echo "2. Checking dnsmasq..."
if sudo systemctl is-active dnsmasq > /dev/null 2>&1; then
echo " ⚠ dnsmasq is running - stopping it..."
sudo systemctl stop dnsmasq
sudo systemctl disable dnsmasq
echo " ✓ dnsmasq stopped"
else
echo " ✓ dnsmasq is not running"
fi
# 3. Restore dnsmasq config if backup exists
echo ""
echo "3. Checking dnsmasq config..."
if [ -f /etc/dnsmasq.conf.backup ]; then
echo " ⚠ Found backup config - restoring..."
sudo cp /etc/dnsmasq.conf.backup /etc/dnsmasq.conf
echo " ✓ Config restored"
else
echo " ✓ No backup config found (normal)"
fi
# 4. Restart NetworkManager to restore DNS
echo ""
echo "4. Restarting NetworkManager..."
sudo systemctl restart NetworkManager
sleep 2
echo " ✓ NetworkManager restarted"
# 5. Check DNS resolution
echo ""
echo "5. Testing DNS resolution..."
if nslookup google.com > /dev/null 2>&1; then
echo " ✓ DNS resolution working"
else
echo " ✗ DNS resolution failed"
echo " → Try: sudo systemctl restart systemd-resolved"
fi
# 6. Test internet connectivity
echo ""
echo "6. Testing internet connectivity..."
if ping -c 2 8.8.8.8 > /dev/null 2>&1; then
echo " ✓ Internet connectivity working"
else
echo " ✗ Internet connectivity failed"
echo " → Check: ip route show"
echo " → Check: ip addr show"
fi
# 7. Check if AP mode is still active
echo ""
echo "7. Checking AP mode status..."
if sudo systemctl is-active hostapd > /dev/null 2>&1; then
echo " ⚠ AP mode is still active!"
echo " → To disable: cd ~/LEDMatrix && python3 -c 'from src.wifi_manager import WiFiManager; wm = WiFiManager(); wm.disable_ap_mode()'"
else
echo " ✓ AP mode is not active"
fi
# 8. Remove any leftover iptables rules
echo ""
echo "8. Checking iptables rules..."
if command -v iptables > /dev/null 2>&1; then
# Try to remove any port 80 redirect rules
sudo iptables -t nat -D PREROUTING -i wlan0 -p tcp --dport 80 -j REDIRECT --to-port 5000 2>/dev/null
sudo iptables -D INPUT -i wlan0 -p tcp --dport 80 -j ACCEPT 2>/dev/null
echo " ✓ Cleaned up iptables rules (if any existed)"
else
echo " → iptables not available (normal on some systems)"
fi
echo ""
echo "=========================================="
echo "Fix Complete"
echo "=========================================="
echo ""
echo "If internet still doesn't work:"
echo "1. Check: ip route show"
echo "2. Check: cat /etc/resolv.conf"
echo "3. Restart network: sudo systemctl restart NetworkManager"
echo "4. Check Ethernet connection: ip link show eth0"
echo ""
-3
View File
@@ -19,9 +19,6 @@ This directory contains scripts for installing and configuring the LEDMatrix sys
(the user who runs the script, i.e. the one you installed LEDMatrix as;
there is no `ledmatrix` system user) the passwordless `nmcli` and related
WiFi permissions the web interface needs
- **`migrate_config.sh`** - Migrates configuration files to new formats (if needed)
- **`debug_install.sh`** - Diagnostic helper used when an install
fails; collects environment info and recent logs
## Usage
-85
View File
@@ -1,85 +0,0 @@
#!/bin/bash
# Quick diagnostic script to check why first_time_install.sh is failing
# Run this on the Pi: bash debug_install.sh
echo "=== Diagnostic Script for Installation Failure ==="
echo ""
echo "1. Checking if running as root:"
if [ "$EUID" -eq 0 ]; then
echo " ✓ Running as root (EUID=$EUID)"
else
echo " ✗ NOT running as root (EUID=$EUID, user=$(whoami))"
fi
echo ""
echo "2. Checking if first_time_install.sh exists:"
if [ -f "./first_time_install.sh" ]; then
echo " ✓ Found ./first_time_install.sh"
echo " Checking if executable:"
if [ -x "./first_time_install.sh" ]; then
echo " ✓ Is executable"
else
echo " ✗ NOT executable (fix with: chmod +x first_time_install.sh)"
fi
else
echo " ✗ NOT found in current directory"
echo " Current directory: $(pwd)"
fi
echo ""
echo "3. Testing argument passing with -y flag:"
echo " Running: bash ./first_time_install.sh -y --help 2>&1 | head -20"
if [ -f "./first_time_install.sh" ]; then
bash ./first_time_install.sh -y --help 2>&1 | head -20 || echo " ✗ Script failed or not found"
else
echo " ✗ first_time_install.sh not found"
fi
echo ""
echo "4. Checking environment variable:"
echo " LEDMATRIX_ASSUME_YES=${LEDMATRIX_ASSUME_YES:-not set}"
echo " Testing with env: env LEDMATRIX_ASSUME_YES=1 bash -c 'echo ASSUME_YES would be set'"
env LEDMATRIX_ASSUME_YES=1 bash -c 'echo " ASSUME_YES would be: ${LEDMATRIX_ASSUME_YES:-not set}"'
echo ""
echo "5. Testing sudo with arguments:"
echo " Command: sudo -E env LEDMATRIX_ASSUME_YES=1 bash ./first_time_install.sh -y --help 2>&1 | head -20"
if [ -f "./first_time_install.sh" ]; then
sudo -E env LEDMATRIX_ASSUME_YES=1 bash ./first_time_install.sh -y --help 2>&1 | head -20 || echo " ✗ Sudo command failed"
else
echo " ✗ first_time_install.sh not found"
fi
echo ""
echo "6. Checking /tmp permissions:"
echo " /tmp is writable: $([ -w /tmp ] && echo 'YES' || echo 'NO')"
echo " /tmp permissions: $(stat -c '%a' /tmp 2>/dev/null || echo 'unknown')"
echo " TMPDIR: ${TMPDIR:-not set}"
echo ""
echo "7. Checking stdin/TTY:"
if [ -t 0 ]; then
echo " ✓ stdin is a TTY (interactive)"
else
echo " ✗ stdin is NOT a TTY (non-interactive/pipe)"
echo " This is expected when running via curl | bash"
fi
echo ""
echo "8. Latest installation log:"
# Determine project root directory (parent of scripts/install/)
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT_DIR="$(cd "$SCRIPT_DIR/../.." && pwd)"
LOG_DIR="$PROJECT_ROOT_DIR/logs"
LOG_FILE=$(ls -t "$LOG_DIR"/first_time_install_*.log 2>/dev/null | head -1)
if [ -n "$LOG_FILE" ]; then
echo " Found: $LOG_FILE"
echo " Last 30 lines:"
tail -30 "$LOG_FILE" | sed 's/^/ /'
else
echo " No log files found in $LOG_DIR/"
fi
echo ""
echo "=== Diagnostic Complete ==="
-43
View File
@@ -1,43 +0,0 @@
#!/bin/bash
# LED Matrix Configuration Migration Script
# This script helps migrate existing config.json to the new template-based system
set -e
echo "=========================================="
echo "LED Matrix Configuration Migration Script"
echo "=========================================="
echo ""
# Check if we're in the right directory
if [ ! -f "config/config.template.json" ]; then
echo "Error: config/config.template.json not found."
echo "Please run this script from the LEDMatrix project root directory."
exit 1
fi
# Check if config.json exists
if [ ! -f "config/config.json" ]; then
echo "No existing config.json found. Creating from template..."
cp config/config.template.json config/config.json
echo "✓ Created config/config.json from template"
echo ""
echo "You can now edit config/config.json with your preferences."
exit 0
fi
echo "Existing config.json found. The system will automatically handle migration."
echo ""
echo "What this means:"
echo "- Your current config.json will be preserved"
echo "- New configuration options will be automatically added with default values"
echo "- A backup will be created before any changes"
echo "- The system handles this automatically when it starts"
echo ""
echo "No manual migration is needed. The ConfigManager will handle everything automatically."
echo ""
echo "To see the latest configuration options, you can reference:"
echo " config/config.template.json"
echo ""
echo "Migration complete!"
-93
View File
@@ -1,93 +0,0 @@
#!/usr/bin/env python3
"""
Setup plugin repository symlinks for local development.
Creates symlinks in plugin-repos/ pointing to plugin directories
in the ledmatrix-plugins monorepo.
"""
import json
import os
import re
import sys
from pathlib import Path
PROJECT_ROOT = Path(__file__).parent.parent
PLUGIN_REPOS_DIR = PROJECT_ROOT / "plugin-repos"
MONOREPO_PLUGINS = PROJECT_ROOT.parent / "ledmatrix-plugins" / "plugins"
def parse_json_with_trailing_commas(text: str) -> dict:
"""Parse JSON that may have trailing commas.
Note: The regex also matches commas inside string values (e.g., "hello, }").
This is fine for manifest files but may corrupt complex JSON with such patterns.
"""
text = re.sub(r",\s*([}\]])", r"\1", text)
return json.loads(text)
def create_symlinks() -> bool:
"""Create symlinks in plugin-repos/ pointing to monorepo plugin dirs."""
if not MONOREPO_PLUGINS.exists():
print(f"Error: Monorepo plugins directory not found: {MONOREPO_PLUGINS}")
return False
PLUGIN_REPOS_DIR.mkdir(exist_ok=True)
created = 0
skipped = 0
print("Setting up plugin symlinks...")
print(f" Source: {MONOREPO_PLUGINS}")
print(f" Links: {PLUGIN_REPOS_DIR}")
print()
for plugin_dir in sorted(MONOREPO_PLUGINS.iterdir()):
if not plugin_dir.is_dir():
continue
manifest_path = plugin_dir / "manifest.json"
if not manifest_path.exists():
continue
try:
with open(manifest_path, "r", encoding="utf-8") as f:
manifest = parse_json_with_trailing_commas(f.read())
except (OSError, json.JSONDecodeError) as e:
print(f" {plugin_dir.name} - failed to read {manifest_path}: {e}")
continue
plugin_id = manifest.get("id", plugin_dir.name)
link_path = PLUGIN_REPOS_DIR / plugin_id
if link_path.exists() or link_path.is_symlink():
if link_path.is_symlink():
try:
if link_path.resolve() == plugin_dir.resolve():
skipped += 1
continue
else:
link_path.unlink()
except OSError:
link_path.unlink()
else:
print(f" {plugin_id} - exists but is not a symlink, skipping")
skipped += 1
continue
relative_path = os.path.relpath(plugin_dir, link_path.parent)
link_path.symlink_to(relative_path)
print(f" {plugin_id} - linked")
created += 1
print(f"\nCreated {created} links, skipped {skipped}")
return True
def main():
print("Setting up plugin repository symlinks from monorepo...\n")
if not create_symlinks():
sys.exit(1)
if __name__ == "__main__":
main()
-2
View File
@@ -7,8 +7,6 @@ This directory contains utility scripts for maintenance and system operations.
- **`clear_cache.py`** - Clears LEDMatrix cache data (specific keys or all cache)
- **`start_web_conditionally.py`** - Conditionally starts the web interface based on config settings
- **`wifi_monitor_daemon.py`** - Background daemon that monitors WiFi/Ethernet connection and manages access point mode
- **`cleanup_venv.sh`** - Cleans up Python virtual environment files
- **`clear_python_cache.sh`** - Clears Python cache files (__pycache__, *.pyc, etc.)
- **`pixlet_config_editor.sh`** - Opens Pixlet's own config UI for one installed Starlark app
- **`apply_dns_single_request.sh`** - Adds `options single-request` to the resolver (run by `ledmatrix-dns-fix.service`)
-23
View File
@@ -1,23 +0,0 @@
#!/bin/bash
# Cleanup script to remove virtual environment if it exists
# This script removes the venv_web_v2 directory if it exists
set -e
# Get the directory where this script is located
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$SCRIPT_DIR"
echo "Cleaning up virtual environment..."
# Check if virtual environment exists and remove it
if [ -d "venv_web_v2" ]; then
echo "Removing existing virtual environment..."
rm -rf venv_web_v2
echo "Virtual environment removed successfully"
else
echo "No virtual environment found to remove"
fi
echo "Cleanup complete!"
-20
View File
@@ -1,20 +0,0 @@
#!/bin/bash
# Clear Python cache files that might be causing import issues
echo "🧹 Clearing Python cache files..."
# Clear __pycache__ directories
find ~/LEDMatrix -type d -name "__pycache__" -exec rm -rf {} + 2>/dev/null || true
# Clear .pyc files
find ~/LEDMatrix -name "*.pyc" -delete 2>/dev/null || true
# Clear Flask session cache if it exists
rm -rf ~/LEDMatrix/web_interface/.webassets-cache 2>/dev/null || true
echo "✅ Python cache cleared"
echo ""
echo "🔄 Now try running the web interface again:"
echo "cd ~/LEDMatrix"
echo "python3 web_interface/start.py"
-164
View File
@@ -1,164 +0,0 @@
#!/bin/bash
# Quick Web UI Verification Script
# Run this via SSH on your Raspberry Pi to verify the web interface is running
echo "=========================================="
echo "Web UI Verification"
echo "=========================================="
echo ""
# The web interface binds port 5000 (web_interface/start.py).
WEB_PORT=5000
PORT_PATTERN=":${WEB_PORT}([^0-9]|$)"
# Colors
GREEN='\033[0;32m'
RED='\033[0;31m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# 1. Check service status
echo "1. Checking service status..."
if systemctl is-active --quiet ledmatrix-web.service 2>/dev/null; then
echo -e "${GREEN}✓${NC} ledmatrix-web.service is running"
# Get detailed status
echo ""
echo "Service details:"
systemctl status ledmatrix-web.service --no-pager -l | head -15
else
echo -e "${RED}✗${NC} ledmatrix-web.service is NOT running"
echo ""
echo "To start the service:"
echo " sudo systemctl start ledmatrix-web.service"
echo ""
fi
echo ""
# 2. Check if port $WEB_PORT is listening
echo "2. Checking if port $WEB_PORT is listening..."
if command -v ss >/dev/null 2>&1; then
if ss -tuln 2>/dev/null | grep -qE "$PORT_PATTERN"; then
echo -e "${GREEN}✓${NC} Port $WEB_PORT is listening"
echo ""
echo "Active connections on port $WEB_PORT:"
ss -tuln | grep -E "$PORT_PATTERN"
else
echo -e "${RED}✗${NC} Port $WEB_PORT is NOT listening"
fi
elif command -v netstat >/dev/null 2>&1; then
if netstat -tuln 2>/dev/null | grep -qE "$PORT_PATTERN"; then
echo -e "${GREEN}✓${NC} Port $WEB_PORT is listening"
echo ""
echo "Active connections on port $WEB_PORT:"
netstat -tuln | grep -E "$PORT_PATTERN"
else
echo -e "${RED}✗${NC} Port $WEB_PORT is NOT listening"
fi
else
echo -e "${YELLOW}⚠${NC} Cannot check port (ss/netstat not available)"
fi
echo ""
# 3. Test HTTP connection
echo "3. Testing HTTP connection..."
if curl -s -o /dev/null -w "%{http_code}" --max-time 5 http://localhost:$WEB_PORT > /dev/null 2>&1; then
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" --max-time 5 http://localhost:$WEB_PORT 2>/dev/null)
if [ "$HTTP_CODE" = "200" ] || [ "$HTTP_CODE" = "302" ] || [ "$HTTP_CODE" = "301" ]; then
echo -e "${GREEN}✓${NC} Web interface is responding (HTTP $HTTP_CODE)"
else
echo -e "${YELLOW}⚠${NC} Web interface responded with HTTP $HTTP_CODE"
fi
else
echo -e "${RED}✗${NC} Cannot connect to web interface on port $WEB_PORT"
fi
echo ""
# 4. Get Pi's IP address
echo "4. Network information..."
IP_ADDRESSES=$(hostname -I 2>/dev/null | awk '{print $1}')
if [ -n "$IP_ADDRESSES" ]; then
echo "Pi IP address(es): $IP_ADDRESSES"
echo ""
echo "Access web interface at:"
for ip in $IP_ADDRESSES; do
echo " http://$ip:$WEB_PORT"
done
else
echo -e "${YELLOW}⚠${NC} Could not determine IP address"
fi
echo ""
# 5. Check recent logs
echo "5. Recent service logs (last 10 lines)..."
echo "----------------------------------------"
journalctl -u ledmatrix-web.service -n 10 --no-pager 2>/dev/null || echo "Could not retrieve logs"
echo ""
# 6. Check for errors in logs
echo "6. Checking for errors in logs..."
ERROR_COUNT=$(journalctl -u ledmatrix-web.service --since "5 minutes ago" --no-pager 2>/dev/null | grep -i "error\|exception\|failed\|traceback" | wc -l)
if [ "$ERROR_COUNT" -gt 0 ]; then
echo -e "${YELLOW}⚠${NC} Found $ERROR_COUNT error(s) in last 5 minutes"
echo ""
echo "Recent errors:"
journalctl -u ledmatrix-web.service --since "5 minutes ago" --no-pager 2>/dev/null | grep -i "error\|exception\|failed\|traceback" | tail -5
else
echo -e "${GREEN}✓${NC} No errors in recent logs"
fi
echo ""
# Summary
echo "=========================================="
echo "Summary"
echo "=========================================="
SERVICE_RUNNING=false
PORT_LISTENING=false
HTTP_RESPONDING=false
if systemctl is-active --quiet ledmatrix-web.service 2>/dev/null; then
SERVICE_RUNNING=true
fi
if (ss -tuln 2>/dev/null | grep -qE "$PORT_PATTERN") || (netstat -tuln 2>/dev/null | grep -qE "$PORT_PATTERN"); then
PORT_LISTENING=true
fi
if curl -s -o /dev/null -w "%{http_code}" --max-time 5 http://localhost:$WEB_PORT > /dev/null 2>&1; then
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" --max-time 5 http://localhost:$WEB_PORT 2>/dev/null)
if [ "$HTTP_CODE" = "200" ] || [ "$HTTP_CODE" = "302" ] || [ "$HTTP_CODE" = "301" ]; then
HTTP_RESPONDING=true
fi
fi
if [ "$SERVICE_RUNNING" = true ] && [ "$PORT_LISTENING" = true ] && [ "$HTTP_RESPONDING" = true ]; then
echo -e "${GREEN}✓ Web UI is running correctly${NC}"
echo ""
echo "You can access it at:"
for ip in $IP_ADDRESSES; do
echo " http://$ip:$WEB_PORT"
done
exit 0
elif [ "$SERVICE_RUNNING" = false ]; then
echo -e "${RED}✗ Web UI service is not running${NC}"
echo ""
echo "To start it:"
echo " sudo systemctl start ledmatrix-web.service"
echo " sudo systemctl enable ledmatrix-web.service # to start on boot"
exit 1
elif [ "$PORT_LISTENING" = false ]; then
echo -e "${RED}✗ Service is running but port $WEB_PORT is not listening${NC}"
echo ""
echo "Check logs for errors:"
echo " sudo journalctl -u ledmatrix-web.service -f"
exit 1
else
echo -e "${YELLOW}⚠ Web UI may have issues${NC}"
echo ""
echo "Check logs for details:"
echo " sudo journalctl -u ledmatrix-web.service -f"
exit 1
fi