mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-10-10 17:16:36 +00:00
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>
This commit is contained in:
@@ -19,7 +19,6 @@ All installation scripts have been moved from the project root to `scripts/insta
|
||||
| `install_wifi_monitor.sh` | `scripts/install/install_wifi_monitor.sh` |
|
||||
| `setup_cache.sh` | `scripts/install/setup_cache.sh` |
|
||||
| `configure_web_sudo.sh` | `scripts/install/configure_web_sudo.sh` |
|
||||
| `migrate_config.sh` | `scripts/install/migrate_config.sh` |
|
||||
|
||||
#### Permission Fix Scripts
|
||||
|
||||
|
||||
@@ -10,11 +10,12 @@ Official plugins live in a single repository,
|
||||
[ledmatrix-plugins](https://github.com/ChuckBuilds/ledmatrix-plugins), with one
|
||||
directory per plugin under `plugins/`. There are no separate per-plugin
|
||||
repositories. For development you clone that monorepo **next to** LEDMatrix
|
||||
and symlink its plugin directories into LEDMatrix's `plugin-repos/`, which is
|
||||
where the plugin loader looks by default.
|
||||
and symlink the plugin directories you are working on into LEDMatrix's
|
||||
`plugins/` directory with `scripts/dev/dev_plugin_setup.sh`.
|
||||
|
||||
- ✅ Plugin code stays in the monorepo checkout, with its own git history
|
||||
- ✅ LEDMatrix discovers the plugins through symlinks in `plugin-repos/`
|
||||
- ✅ LEDMatrix discovers the plugins through symlinks in `plugins/`
|
||||
(git-ignored), so the production `plugin-repos/` directory is untouched
|
||||
- ✅ `LEDMatrix.code-workspace` opens both repositories in VS Code/Cursor
|
||||
|
||||
## Directory Structure
|
||||
@@ -22,12 +23,11 @@ where the plugin loader looks by default.
|
||||
```text
|
||||
~/Github/
|
||||
├── LEDMatrix/ # Main project
|
||||
│ ├── plugin-repos/ # Plugin directory the loader scans
|
||||
│ │ ├── starlark-apps/ # Bundled with LEDMatrix (tracked in git)
|
||||
│ │ ├── web-ui-info/ # Bundled with LEDMatrix (tracked in git)
|
||||
│ │ ├── clock-simple -> ../../ledmatrix-plugins/plugins/clock-simple
|
||||
│ │ ├── ledmatrix-weather -> ../../ledmatrix-plugins/plugins/ledmatrix-weather
|
||||
│ ├── plugins/ # Dev plugin directory (git-ignored)
|
||||
│ │ ├── clock-simple -> ~/Github/ledmatrix-plugins/plugins/clock-simple
|
||||
│ │ ├── ledmatrix-weather -> ~/Github/ledmatrix-plugins/plugins/ledmatrix-weather
|
||||
│ │ └── ...
|
||||
│ ├── plugin-repos/ # Default (Plugin Store) plugin directory
|
||||
│ ├── LEDMatrix.code-workspace # Opens LEDMatrix and ../ledmatrix-plugins
|
||||
│ └── ...
|
||||
└── ledmatrix-plugins/ # Plugin monorepo (git repo)
|
||||
@@ -44,61 +44,65 @@ where the plugin loader looks by default.
|
||||
### 1. The plugin monorepo
|
||||
|
||||
Clone ledmatrix-plugins into the same parent directory as LEDMatrix (the
|
||||
scripts below look for `../ledmatrix-plugins` relative to the LEDMatrix
|
||||
root):
|
||||
workspace file and `scripts/update_plugin_repos.py` look for
|
||||
`../ledmatrix-plugins` relative to the LEDMatrix root):
|
||||
|
||||
```bash
|
||||
cd ~/Github
|
||||
git clone https://github.com/ChuckBuilds/ledmatrix-plugins.git
|
||||
```
|
||||
|
||||
### 2. Symlinks in plugin-repos/
|
||||
### 2. Symlinks in plugins/
|
||||
|
||||
`scripts/setup_plugin_repos.py` creates one symlink per plugin in
|
||||
`LEDMatrix/plugin-repos/`, named after the plugin's manifest `id` and pointing
|
||||
at `../ledmatrix-plugins/plugins/<dir>`.
|
||||
`scripts/dev/dev_plugin_setup.sh link <name> <path>` creates
|
||||
`LEDMatrix/plugins/<name>` as a symlink to a plugin directory. Use the
|
||||
plugin's manifest `id` as the name: that is the name the loader and
|
||||
`config.json` use, and the script warns when the two differ.
|
||||
|
||||
### 3. Multi-root workspace
|
||||
|
||||
`LEDMatrix.code-workspace` has two roots: LEDMatrix itself and
|
||||
`../ledmatrix-plugins`.
|
||||
|
||||
## Setup Scripts
|
||||
## Setup
|
||||
|
||||
### Initial Setup
|
||||
### Link plugins
|
||||
|
||||
```bash
|
||||
cd ~/Github/LEDMatrix
|
||||
python3 scripts/setup_plugin_repos.py
|
||||
./scripts/dev/dev_plugin_setup.sh link clock-simple ../ledmatrix-plugins/plugins/clock-simple
|
||||
./scripts/dev/dev_plugin_setup.sh list # show what is linked
|
||||
```
|
||||
|
||||
This script:
|
||||
- Reads each `manifest.json` under `../ledmatrix-plugins/plugins/`
|
||||
- Creates `plugin-repos/<id>` symlinks (relative) to those directories
|
||||
- Leaves correct links alone, replaces links that point elsewhere, and skips
|
||||
(does not overwrite) a real directory of the same name — for example a
|
||||
plugin you installed from the Plugin Store. Remove that directory first if
|
||||
you want the linked copy.
|
||||
If a real (non-symlink) directory of the same name already exists in
|
||||
`plugins/`, the script offers to back it up and replace it.
|
||||
|
||||
Without a sibling checkout, `./scripts/dev/dev_plugin_setup.sh link-github
|
||||
<name>` clones the monorepo into `~/.ledmatrix-dev-plugins/` instead and links
|
||||
the plugin from there. See the
|
||||
[Plugin Development Guide](PLUGIN_DEVELOPMENT_GUIDE.md).
|
||||
|
||||
### Updating Plugins
|
||||
|
||||
```bash
|
||||
cd ~/Github/LEDMatrix
|
||||
python3 scripts/update_plugin_repos.py
|
||||
python3 scripts/update_plugin_repos.py # git pull in ../ledmatrix-plugins
|
||||
# or
|
||||
./scripts/dev/dev_plugin_setup.sh update # git pull in every linked checkout
|
||||
```
|
||||
|
||||
This runs `git pull` in `../ledmatrix-plugins` and prints the result. The
|
||||
symlinks pick up the new code; restart the display to load it.
|
||||
The symlinks pick up the new code; restart the display to load it.
|
||||
|
||||
## Configuration
|
||||
|
||||
The loader reads plugins from `plugin_system.plugins_directory` in
|
||||
`config/config.json`. The default is already right for this setup:
|
||||
The loader scans only `plugin_system.plugins_directory` in
|
||||
`config/config.json` (default `plugin-repos`). Point it at `plugins` so it
|
||||
finds the links:
|
||||
|
||||
```json
|
||||
{
|
||||
"plugin_system": {
|
||||
"plugins_directory": "plugin-repos"
|
||||
"plugins_directory": "plugins"
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -117,7 +121,7 @@ The loader reads plugins from `plugin_system.plugins_directory` in
|
||||
### Adding New Plugins
|
||||
|
||||
1. Create `plugins/<your-plugin-id>/` in the monorepo checkout
|
||||
2. Run `python3 scripts/setup_plugin_repos.py` in LEDMatrix to link it
|
||||
2. Link it: `./scripts/dev/dev_plugin_setup.sh link <your-plugin-id> ../ledmatrix-plugins/plugins/<your-plugin-id>`
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
@@ -125,30 +129,21 @@ The loader reads plugins from `plugin_system.plugins_directory` in
|
||||
|
||||
```bash
|
||||
cd ~/Github/LEDMatrix
|
||||
ls -la plugin-repos/ # links present and not broken?
|
||||
python3 scripts/setup_plugin_repos.py # recreate them
|
||||
ls -la plugins/ # links present and not broken?
|
||||
./scripts/dev/dev_plugin_setup.sh status # link targets and git state
|
||||
```
|
||||
|
||||
Also check that `plugin_system.plugins_directory` is `plugin-repos`.
|
||||
|
||||
### "Monorepo plugins directory not found"
|
||||
|
||||
`setup_plugin_repos.py` expects the monorepo at `../ledmatrix-plugins`. Clone
|
||||
it there (or symlink it there).
|
||||
Also check that `plugin_system.plugins_directory` is `plugins`.
|
||||
|
||||
### Plugin updates not showing
|
||||
|
||||
1. Verify the link target: `ls -la plugin-repos/<id>`
|
||||
1. Verify the link target: `ls -la plugins/<id>`
|
||||
2. Check that you're editing the monorepo checkout, not a store-installed copy
|
||||
3. Restart the LEDMatrix service (or `run.py`)
|
||||
|
||||
## Notes
|
||||
|
||||
- `plugin-repos/` is tracked in git only for the bundled plugins
|
||||
(`starlark-apps`, `web-ui-info`). The symlinks you create are untracked
|
||||
files; don't commit them.
|
||||
- For linking a single plugin into `plugins/` instead (without a sibling
|
||||
checkout), see `scripts/dev/dev_plugin_setup.sh` in the
|
||||
[Plugin Development Guide](PLUGIN_DEVELOPMENT_GUIDE.md).
|
||||
- `plugins/` is git-ignored (except `plugins/.gitkeep`); the symlinks are
|
||||
never committed.
|
||||
- When changing a plugin in the monorepo, bump its manifest `version` and run
|
||||
`python update_registry.py`, or users won't receive the update.
|
||||
|
||||
@@ -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
|
||||
@@ -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,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>
|
||||
```
|
||||
|
||||
|
||||
@@ -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)
|
||||
@@ -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 ""
|
||||
|
||||
@@ -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 "=========================================="
|
||||
|
||||
@@ -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()
|
||||
@@ -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 ""
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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 ==="
|
||||
@@ -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!"
|
||||
@@ -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()
|
||||
@@ -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`)
|
||||
|
||||
|
||||
@@ -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!"
|
||||
@@ -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"
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user