mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-10-04 06:15:09 +00:00
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:
@@ -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