persistent config file via config.template.json and migrate_config.sh
2
.gitignore
vendored
@@ -5,6 +5,8 @@ __pycache__/
|
|||||||
|
|
||||||
# Secrets
|
# Secrets
|
||||||
config/config_secrets.json
|
config/config_secrets.json
|
||||||
|
config/config.json
|
||||||
|
config/config.json.backup
|
||||||
credentials.json
|
credentials.json
|
||||||
token.pickle
|
token.pickle
|
||||||
|
|
||||||
|
|||||||
34
README.md
@@ -260,16 +260,38 @@ sudo reboot
|
|||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
1.Edit `config/config.json` with your preferences via `sudo nano config/config.json`
|
### Initial Setup
|
||||||
|
|
||||||
###API Keys
|
The system uses a template-based configuration approach to avoid Git conflicts during updates:
|
||||||
|
|
||||||
|
1. **First-time setup**: Copy the template to create your config:
|
||||||
|
```bash
|
||||||
|
cp config/config.template.json config/config.json
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Edit your configuration**:
|
||||||
|
```bash
|
||||||
|
sudo nano config/config.json
|
||||||
|
```
|
||||||
|
or edit via web interface at http://ledpi:5001
|
||||||
|
|
||||||
|
|
||||||
|
### API Keys and Secrets
|
||||||
|
|
||||||
For sensitive settings like API keys:
|
For sensitive settings like API keys:
|
||||||
Copy the template: `cp config/config_secrets.template.json config/config_secrets.json`
|
1. Copy the secrets template: `cp config/config_secrets.template.json config/config_secrets.json`
|
||||||
Edit `config/config_secrets.json` with your API keys via `sudo nano config/config_secrets.json`
|
2. Edit `config/config_secrets.json` with your API keys via `sudo nano config/config_secrets.json`
|
||||||
Ctrl + X to exit, Y to overwrite, Enter to Confirm
|
3. Ctrl + X to exit, Y to overwrite, Enter to Confirm
|
||||||
|
|
||||||
Everything is configured via `config/config.json` and `config/config_secrets.json`.
|
### Automatic Configuration Migration
|
||||||
|
|
||||||
|
The system automatically handles configuration updates:
|
||||||
|
- **New installations**: Creates `config.json` from the template automatically
|
||||||
|
- **Existing installations**: Automatically adds new configuration options with default values when the system starts
|
||||||
|
- **Backup protection**: Creates a backup of your current config before applying updates
|
||||||
|
- **No conflicts**: Your custom settings are preserved while new options are added
|
||||||
|
|
||||||
|
Everything is configured via `config/config.json` and `config/config_secrets.json`. The `config.json` file is not tracked by Git to prevent conflicts during updates.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -5,10 +5,10 @@
|
|||||||
"start_time": "07:00",
|
"start_time": "07:00",
|
||||||
"end_time": "23:00"
|
"end_time": "23:00"
|
||||||
},
|
},
|
||||||
"timezone": "America/New_York",
|
"timezone": "America/Chicago",
|
||||||
"location": {
|
"location": {
|
||||||
"city": "Tampa",
|
"city": "Dallas",
|
||||||
"state": "Florida",
|
"state": "Texas",
|
||||||
"country": "US"
|
"country": "US"
|
||||||
},
|
},
|
||||||
"display": {
|
"display": {
|
||||||
@@ -39,7 +39,6 @@
|
|||||||
"daily_forecast": 30,
|
"daily_forecast": 30,
|
||||||
"stock_news": 20,
|
"stock_news": 20,
|
||||||
"odds_ticker": 60,
|
"odds_ticker": 60,
|
||||||
"leaderboard": 60,
|
|
||||||
"nhl_live": 30,
|
"nhl_live": 30,
|
||||||
"nhl_recent": 30,
|
"nhl_recent": 30,
|
||||||
"nhl_upcoming": 30,
|
"nhl_upcoming": 30,
|
||||||
@@ -82,10 +81,10 @@
|
|||||||
"update_interval": 1
|
"update_interval": 1
|
||||||
},
|
},
|
||||||
"weather": {
|
"weather": {
|
||||||
"enabled": true,
|
"enabled": false,
|
||||||
"update_interval": 1800,
|
"update_interval": 1800,
|
||||||
"units": "imperial",
|
"units": "imperial",
|
||||||
"display_format": "{temp}\u00b0F\n{condition}"
|
"display_format": "{temp}°F\n{condition}"
|
||||||
},
|
},
|
||||||
"stocks": {
|
"stocks": {
|
||||||
"enabled": false,
|
"enabled": false,
|
||||||
@@ -130,12 +129,11 @@
|
|||||||
"duration_buffer": 0.1
|
"duration_buffer": 0.1
|
||||||
},
|
},
|
||||||
"odds_ticker": {
|
"odds_ticker": {
|
||||||
"enabled": false,
|
"enabled": true,
|
||||||
"show_favorite_teams_only": true,
|
"show_favorite_teams_only": true,
|
||||||
"games_per_favorite_team": 1,
|
"games_per_favorite_team": 1,
|
||||||
"max_games_per_league": 5,
|
"max_games_per_league": 5,
|
||||||
"show_odds_only": false,
|
"show_odds_only": false,
|
||||||
"fetch_odds": true,
|
|
||||||
"sort_order": "soonest",
|
"sort_order": "soonest",
|
||||||
"enabled_leagues": [
|
"enabled_leagues": [
|
||||||
"nfl",
|
"nfl",
|
||||||
@@ -152,46 +150,6 @@
|
|||||||
"dynamic_duration": true,
|
"dynamic_duration": true,
|
||||||
"min_duration": 30,
|
"min_duration": 30,
|
||||||
"max_duration": 300,
|
"max_duration": 300,
|
||||||
"duration_buffer": 0.05
|
|
||||||
},
|
|
||||||
"leaderboard": {
|
|
||||||
"enabled": false,
|
|
||||||
"enabled_sports": {
|
|
||||||
"nfl": {
|
|
||||||
"enabled": true,
|
|
||||||
"top_teams": 10
|
|
||||||
},
|
|
||||||
"nba": {
|
|
||||||
"enabled": false,
|
|
||||||
"top_teams": 10
|
|
||||||
},
|
|
||||||
"mlb": {
|
|
||||||
"enabled": false,
|
|
||||||
"top_teams": 10
|
|
||||||
},
|
|
||||||
"ncaa_fb": {
|
|
||||||
"enabled": true,
|
|
||||||
"top_teams": 25,
|
|
||||||
"show_ranking": true
|
|
||||||
},
|
|
||||||
"nhl": {
|
|
||||||
"enabled": false,
|
|
||||||
"top_teams": 10
|
|
||||||
},
|
|
||||||
"ncaam_basketball": {
|
|
||||||
"enabled": false,
|
|
||||||
"top_teams": 25
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"update_interval": 3600,
|
|
||||||
"scroll_speed": 1,
|
|
||||||
"scroll_delay": 0.01,
|
|
||||||
"display_duration": 60,
|
|
||||||
"loop": false,
|
|
||||||
"request_timeout": 30,
|
|
||||||
"dynamic_duration": true,
|
|
||||||
"min_duration": 45,
|
|
||||||
"max_duration": 600,
|
|
||||||
"duration_buffer": 0.1
|
"duration_buffer": 0.1
|
||||||
},
|
},
|
||||||
"calendar": {
|
"calendar": {
|
||||||
@@ -264,6 +222,8 @@
|
|||||||
"live_update_interval": 30,
|
"live_update_interval": 30,
|
||||||
"live_odds_update_interval": 3600,
|
"live_odds_update_interval": 3600,
|
||||||
"odds_update_interval": 3600,
|
"odds_update_interval": 3600,
|
||||||
|
"recent_update_interval": 3600,
|
||||||
|
"upcoming_update_interval": 3600,
|
||||||
"recent_games_to_show": 1,
|
"recent_games_to_show": 1,
|
||||||
"upcoming_games_to_show": 1,
|
"upcoming_games_to_show": 1,
|
||||||
"show_favorite_teams_only": true,
|
"show_favorite_teams_only": true,
|
||||||
@@ -299,10 +259,9 @@
|
|||||||
],
|
],
|
||||||
"logo_dir": "assets/sports/ncaa_fbs_logos",
|
"logo_dir": "assets/sports/ncaa_fbs_logos",
|
||||||
"show_records": true,
|
"show_records": true,
|
||||||
"show_ranking": true,
|
|
||||||
"display_modes": {
|
"display_modes": {
|
||||||
"ncaa_fb_live": true,
|
"ncaa_fb_live": true,
|
||||||
"ncaa_fb_recent": true ,
|
"ncaa_fb_recent": true,
|
||||||
"ncaa_fb_upcoming": true
|
"ncaa_fb_upcoming": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -409,7 +368,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"text_display": {
|
"text_display": {
|
||||||
"enabled": true,
|
"enabled": false,
|
||||||
"text": "Subscribe to ChuckBuilds",
|
"text": "Subscribe to ChuckBuilds",
|
||||||
"font_path": "assets/fonts/press-start-2p.ttf",
|
"font_path": "assets/fonts/press-start-2p.ttf",
|
||||||
"font_size": 8,
|
"font_size": 8,
|
||||||
@@ -150,17 +150,18 @@ echo ""
|
|||||||
echo "This script will perform the following steps:"
|
echo "This script will perform the following steps:"
|
||||||
echo "1. Install system dependencies"
|
echo "1. Install system dependencies"
|
||||||
echo "2. Fix cache permissions"
|
echo "2. Fix cache permissions"
|
||||||
echo "3. Install main LED Matrix service"
|
echo "3. Fix assets directory permissions"
|
||||||
echo "4. Install Python project dependencies (requirements.txt)"
|
echo "4. Install main LED Matrix service"
|
||||||
echo "5. Build and install rpi-rgb-led-matrix and test import"
|
echo "5. Install Python project dependencies (requirements.txt)"
|
||||||
echo "6. Install web interface dependencies"
|
echo "6. Build and install rpi-rgb-led-matrix and test import"
|
||||||
echo "7. Install web interface service"
|
echo "7. Install web interface dependencies"
|
||||||
echo "8. Configure web interface permissions"
|
echo "8. Install web interface service"
|
||||||
echo "9. Configure passwordless sudo access"
|
echo "9. Configure web interface permissions"
|
||||||
echo "10. Set up proper file ownership"
|
echo "10. Configure passwordless sudo access"
|
||||||
echo "11. Configure sound module to avoid conflicts"
|
echo "11. Set up proper file ownership"
|
||||||
echo "12. Apply performance optimizations"
|
echo "12. Configure sound module to avoid conflicts"
|
||||||
echo "13. Test the installation"
|
echo "13. Apply performance optimizations"
|
||||||
|
echo "14. Test the installation"
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
# Ask for confirmation
|
# Ask for confirmation
|
||||||
@@ -217,8 +218,57 @@ else
|
|||||||
fi
|
fi
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
|
CURRENT_STEP="Fix assets directory permissions"
|
||||||
|
echo "Step 3: Fixing assets directory permissions..."
|
||||||
|
echo "--------------------------------------------"
|
||||||
|
|
||||||
|
# Run the assets permissions fix
|
||||||
|
if [ -f "$PROJECT_ROOT_DIR/fix_assets_permissions.sh" ]; then
|
||||||
|
echo "Running assets permissions fix..."
|
||||||
|
bash "$PROJECT_ROOT_DIR/fix_assets_permissions.sh"
|
||||||
|
echo "✓ Assets permissions fixed"
|
||||||
|
else
|
||||||
|
echo "⚠ Assets permissions script not found, fixing permissions manually..."
|
||||||
|
|
||||||
|
# Set ownership of the entire assets directory to the real user
|
||||||
|
echo "Setting ownership of assets directory..."
|
||||||
|
chown -R "$ACTUAL_USER:$ACTUAL_USER" "$PROJECT_ROOT_DIR/assets"
|
||||||
|
|
||||||
|
# Set permissions to allow read/write for owner and group, read for others
|
||||||
|
echo "Setting permissions for assets directory..."
|
||||||
|
chmod -R 775 "$PROJECT_ROOT_DIR/assets"
|
||||||
|
|
||||||
|
# Specifically ensure the sports logos directories are writable
|
||||||
|
SPORTS_DIRS=(
|
||||||
|
"sports/ncaa_fbs_logos"
|
||||||
|
"sports/nfl_logos"
|
||||||
|
"sports/nba_logos"
|
||||||
|
"sports/nhl_logos"
|
||||||
|
"sports/mlb_logos"
|
||||||
|
"sports/milb_logos"
|
||||||
|
"sports/soccer_logos"
|
||||||
|
)
|
||||||
|
|
||||||
|
echo "Ensuring sports logo directories are writable..."
|
||||||
|
for SPORTS_DIR in "${SPORTS_DIRS[@]}"; do
|
||||||
|
FULL_PATH="$PROJECT_ROOT_DIR/assets/$SPORTS_DIR"
|
||||||
|
if [ -d "$FULL_PATH" ]; then
|
||||||
|
chmod 775 "$FULL_PATH"
|
||||||
|
chown "$ACTUAL_USER:$ACTUAL_USER" "$FULL_PATH"
|
||||||
|
else
|
||||||
|
echo "Creating directory: $FULL_PATH"
|
||||||
|
mkdir -p "$FULL_PATH"
|
||||||
|
chown "$ACTUAL_USER:$ACTUAL_USER" "$FULL_PATH"
|
||||||
|
chmod 775 "$FULL_PATH"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "✓ Assets permissions fixed manually"
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
|
||||||
CURRENT_STEP="Install main LED Matrix service"
|
CURRENT_STEP="Install main LED Matrix service"
|
||||||
echo "Step 3: Installing main LED Matrix service..."
|
echo "Step 4: Installing main LED Matrix service..."
|
||||||
echo "---------------------------------------------"
|
echo "---------------------------------------------"
|
||||||
|
|
||||||
# Run the main service installation (idempotent)
|
# Run the main service installation (idempotent)
|
||||||
@@ -233,14 +283,52 @@ else
|
|||||||
fi
|
fi
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
CURRENT_STEP="Ensure secrets configuration exists"
|
CURRENT_STEP="Ensure configuration files exist"
|
||||||
echo "Step 3.1: Ensuring secrets configuration exists..."
|
echo "Step 4.1: Ensuring configuration files exist..."
|
||||||
echo "-----------------------------------------------"
|
echo "------------------------------------------------"
|
||||||
|
|
||||||
# Ensure config directory exists
|
# Ensure config directory exists
|
||||||
mkdir -p "$PROJECT_ROOT_DIR/config"
|
mkdir -p "$PROJECT_ROOT_DIR/config"
|
||||||
chmod 755 "$PROJECT_ROOT_DIR/config" || true
|
chmod 755 "$PROJECT_ROOT_DIR/config" || true
|
||||||
|
|
||||||
|
# Create config.json from template if missing
|
||||||
|
if [ ! -f "$PROJECT_ROOT_DIR/config/config.json" ]; then
|
||||||
|
if [ -f "$PROJECT_ROOT_DIR/config/config.template.json" ]; then
|
||||||
|
echo "Creating config/config.json from template..."
|
||||||
|
cp "$PROJECT_ROOT_DIR/config/config.template.json" "$PROJECT_ROOT_DIR/config/config.json"
|
||||||
|
chown "$ACTUAL_USER:$ACTUAL_USER" "$PROJECT_ROOT_DIR/config/config.json" || true
|
||||||
|
chmod 644 "$PROJECT_ROOT_DIR/config/config.json"
|
||||||
|
echo "✓ Main config file created from template"
|
||||||
|
else
|
||||||
|
echo "⚠ Template config/config.template.json not found; creating a minimal config file"
|
||||||
|
cat > "$PROJECT_ROOT_DIR/config/config.json" <<'EOF'
|
||||||
|
{
|
||||||
|
"web_display_autostart": true,
|
||||||
|
"timezone": "America/Chicago",
|
||||||
|
"display": {
|
||||||
|
"hardware": {
|
||||||
|
"rows": 32,
|
||||||
|
"cols": 64,
|
||||||
|
"chain_length": 2,
|
||||||
|
"parallel": 1,
|
||||||
|
"brightness": 95,
|
||||||
|
"hardware_mapping": "adafruit-hat-pwm"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"clock": {
|
||||||
|
"enabled": true,
|
||||||
|
"format": "%I:%M %p"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
chown "$ACTUAL_USER:$ACTUAL_USER" "$PROJECT_ROOT_DIR/config/config.json" || true
|
||||||
|
chmod 644 "$PROJECT_ROOT_DIR/config/config.json"
|
||||||
|
echo "✓ Minimal config file created"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "✓ Main config file already exists"
|
||||||
|
fi
|
||||||
|
|
||||||
# Create config_secrets.json from template if missing
|
# Create config_secrets.json from template if missing
|
||||||
if [ ! -f "$PROJECT_ROOT_DIR/config/config_secrets.json" ]; then
|
if [ ! -f "$PROJECT_ROOT_DIR/config/config_secrets.json" ]; then
|
||||||
if [ -f "$PROJECT_ROOT_DIR/config/config_secrets.template.json" ]; then
|
if [ -f "$PROJECT_ROOT_DIR/config/config_secrets.template.json" ]; then
|
||||||
@@ -263,12 +351,12 @@ EOF
|
|||||||
echo "✓ Minimal secrets file created"
|
echo "✓ Minimal secrets file created"
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
echo "Secrets file already exists; leaving as-is"
|
echo "✓ Secrets file already exists"
|
||||||
fi
|
fi
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
CURRENT_STEP="Install project Python dependencies"
|
CURRENT_STEP="Install project Python dependencies"
|
||||||
echo "Step 4: Installing Python project dependencies..."
|
echo "Step 5: Installing Python project dependencies..."
|
||||||
echo "-----------------------------------------------"
|
echo "-----------------------------------------------"
|
||||||
|
|
||||||
# Install main project Python dependencies
|
# Install main project Python dependencies
|
||||||
@@ -283,7 +371,7 @@ echo "✓ Project Python dependencies installed"
|
|||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
CURRENT_STEP="Build and install rpi-rgb-led-matrix"
|
CURRENT_STEP="Build and install rpi-rgb-led-matrix"
|
||||||
echo "Step 5: Building and installing rpi-rgb-led-matrix..."
|
echo "Step 6: Building and installing rpi-rgb-led-matrix..."
|
||||||
echo "-----------------------------------------------------"
|
echo "-----------------------------------------------------"
|
||||||
|
|
||||||
# If already installed and not forcing rebuild, skip expensive build
|
# If already installed and not forcing rebuild, skip expensive build
|
||||||
@@ -327,7 +415,7 @@ fi
|
|||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
CURRENT_STEP="Install web interface dependencies"
|
CURRENT_STEP="Install web interface dependencies"
|
||||||
echo "Step 6: Installing web interface dependencies..."
|
echo "Step 7: Installing web interface dependencies..."
|
||||||
echo "------------------------------------------------"
|
echo "------------------------------------------------"
|
||||||
|
|
||||||
# Install web interface dependencies
|
# Install web interface dependencies
|
||||||
@@ -335,9 +423,9 @@ echo "Installing Python dependencies for web interface..."
|
|||||||
cd "$PROJECT_ROOT_DIR"
|
cd "$PROJECT_ROOT_DIR"
|
||||||
|
|
||||||
# Try to install dependencies using the smart installer if available
|
# Try to install dependencies using the smart installer if available
|
||||||
if [ -f "$PROJECT_ROOT_DIR/install_dependencies_apt.py" ]; then
|
if [ -f "$PROJECT_ROOT_DIR/scripts/install_dependencies_apt.py" ]; then
|
||||||
echo "Using smart dependency installer..."
|
echo "Using smart dependency installer..."
|
||||||
python3 "$PROJECT_ROOT_DIR/install_dependencies_apt.py"
|
python3 "$PROJECT_ROOT_DIR/scripts/install_dependencies_apt.py"
|
||||||
else
|
else
|
||||||
echo "Using pip to install dependencies..."
|
echo "Using pip to install dependencies..."
|
||||||
if [ -f "$PROJECT_ROOT_DIR/requirements_web_v2.txt" ]; then
|
if [ -f "$PROJECT_ROOT_DIR/requirements_web_v2.txt" ]; then
|
||||||
@@ -351,7 +439,7 @@ echo "✓ Web interface dependencies installed"
|
|||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
CURRENT_STEP="Install web interface service"
|
CURRENT_STEP="Install web interface service"
|
||||||
echo "Step 7: Installing web interface service..."
|
echo "Step 8: Installing web interface service..."
|
||||||
echo "-------------------------------------------"
|
echo "-------------------------------------------"
|
||||||
|
|
||||||
if [ -f "$PROJECT_ROOT_DIR/install_web_service.sh" ]; then
|
if [ -f "$PROJECT_ROOT_DIR/install_web_service.sh" ]; then
|
||||||
@@ -369,7 +457,7 @@ fi
|
|||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
CURRENT_STEP="Harden systemd unit file permissions"
|
CURRENT_STEP="Harden systemd unit file permissions"
|
||||||
echo "Step 7.1: Setting systemd unit file permissions..."
|
echo "Step 8.1: Setting systemd unit file permissions..."
|
||||||
echo "-----------------------------------------------"
|
echo "-----------------------------------------------"
|
||||||
for unit in "/etc/systemd/system/ledmatrix.service" "/etc/systemd/system/ledmatrix-web.service"; do
|
for unit in "/etc/systemd/system/ledmatrix.service" "/etc/systemd/system/ledmatrix-web.service"; do
|
||||||
if [ -f "$unit" ]; then
|
if [ -f "$unit" ]; then
|
||||||
@@ -382,7 +470,7 @@ echo "✓ Systemd unit file permissions set"
|
|||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
CURRENT_STEP="Configure web interface permissions"
|
CURRENT_STEP="Configure web interface permissions"
|
||||||
echo "Step 8: Configuring web interface permissions..."
|
echo "Step 9: Configuring web interface permissions..."
|
||||||
echo "------------------------------------------------"
|
echo "------------------------------------------------"
|
||||||
|
|
||||||
# Add user to required groups (idempotent)
|
# Add user to required groups (idempotent)
|
||||||
@@ -404,7 +492,7 @@ echo "✓ User added to required groups"
|
|||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
CURRENT_STEP="Configure passwordless sudo access"
|
CURRENT_STEP="Configure passwordless sudo access"
|
||||||
echo "Step 9: Configuring passwordless sudo access..."
|
echo "Step 10: Configuring passwordless sudo access..."
|
||||||
echo "------------------------------------------------"
|
echo "------------------------------------------------"
|
||||||
|
|
||||||
# Create sudoers configuration for the web interface
|
# Create sudoers configuration for the web interface
|
||||||
@@ -451,7 +539,7 @@ echo "✓ Passwordless sudo access configured"
|
|||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
CURRENT_STEP="Set proper file ownership"
|
CURRENT_STEP="Set proper file ownership"
|
||||||
echo "Step 10: Setting proper file ownership..."
|
echo "Step 11: Setting proper file ownership..."
|
||||||
echo "----------------------------------------"
|
echo "----------------------------------------"
|
||||||
|
|
||||||
# Set ownership of project files to the user
|
# Set ownership of project files to the user
|
||||||
@@ -475,7 +563,7 @@ echo "✓ File ownership configured"
|
|||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
CURRENT_STEP="Normalize project file permissions"
|
CURRENT_STEP="Normalize project file permissions"
|
||||||
echo "Step 10.1: Normalizing project file and directory permissions..."
|
echo "Step 11.1: Normalizing project file and directory permissions..."
|
||||||
echo "--------------------------------------------------------------"
|
echo "--------------------------------------------------------------"
|
||||||
|
|
||||||
# Normalize directory permissions (exclude VCS metadata)
|
# Normalize directory permissions (exclude VCS metadata)
|
||||||
@@ -489,14 +577,14 @@ find "$PROJECT_ROOT_DIR" -path "*/.git*" -prune -o -type f -name "*.sh" -exec ch
|
|||||||
|
|
||||||
# Explicitly ensure common helper scripts are executable (in case paths change)
|
# Explicitly ensure common helper scripts are executable (in case paths change)
|
||||||
chmod 755 "$PROJECT_ROOT_DIR/start_display.sh" "$PROJECT_ROOT_DIR/stop_display.sh" 2>/dev/null || true
|
chmod 755 "$PROJECT_ROOT_DIR/start_display.sh" "$PROJECT_ROOT_DIR/stop_display.sh" 2>/dev/null || true
|
||||||
chmod 755 "$PROJECT_ROOT_DIR/fix_cache_permissions.sh" "$PROJECT_ROOT_DIR/fix_web_permissions.sh" 2>/dev/null || true
|
chmod 755 "$PROJECT_ROOT_DIR/fix_cache_permissions.sh" "$PROJECT_ROOT_DIR/fix_web_permissions.sh" "$PROJECT_ROOT_DIR/fix_assets_permissions.sh" 2>/dev/null || true
|
||||||
chmod 755 "$PROJECT_ROOT_DIR/install_service.sh" "$PROJECT_ROOT_DIR/install_web_service.sh" 2>/dev/null || true
|
chmod 755 "$PROJECT_ROOT_DIR/install_service.sh" "$PROJECT_ROOT_DIR/install_web_service.sh" 2>/dev/null || true
|
||||||
|
|
||||||
echo "✓ Project file permissions normalized"
|
echo "✓ Project file permissions normalized"
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
CURRENT_STEP="Sound module configuration"
|
CURRENT_STEP="Sound module configuration"
|
||||||
echo "Step 11: Sound module configuration..."
|
echo "Step 12: Sound module configuration..."
|
||||||
echo "-------------------------------------"
|
echo "-------------------------------------"
|
||||||
|
|
||||||
# Remove services that may interfere with LED matrix timing
|
# Remove services that may interfere with LED matrix timing
|
||||||
@@ -539,7 +627,7 @@ echo "✓ Sound module configuration applied"
|
|||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
CURRENT_STEP="Apply performance optimizations"
|
CURRENT_STEP="Apply performance optimizations"
|
||||||
echo "Step 12: Applying performance optimizations..."
|
echo "Step 13: Applying performance optimizations..."
|
||||||
echo "---------------------------------------------"
|
echo "---------------------------------------------"
|
||||||
|
|
||||||
# Prefer /boot/firmware on newer Raspberry Pi OS, fall back to /boot on older
|
# Prefer /boot/firmware on newer Raspberry Pi OS, fall back to /boot on older
|
||||||
@@ -588,7 +676,7 @@ echo "✓ Performance optimizations applied"
|
|||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
CURRENT_STEP="Test the installation"
|
CURRENT_STEP="Test the installation"
|
||||||
echo "Step 13: Testing the installation..."
|
echo "Step 14: Testing the installation..."
|
||||||
echo "----------------------------------"
|
echo "----------------------------------"
|
||||||
|
|
||||||
# Test sudo access
|
# Test sudo access
|
||||||
@@ -665,7 +753,8 @@ echo "Enable/disable web interface autostart:"
|
|||||||
echo " Edit config/config.json and set 'web_display_autostart': true"
|
echo " Edit config/config.json and set 'web_display_autostart': true"
|
||||||
echo ""
|
echo ""
|
||||||
echo "Configuration files:"
|
echo "Configuration files:"
|
||||||
echo " Main config: config/config.json"
|
echo " Main config: config/config.json (created from template automatically)"
|
||||||
echo " Secrets: config/config_secrets.json (create from template if needed)"
|
echo " Secrets: config/config_secrets.json (created from template automatically)"
|
||||||
|
echo " Template: config/config.template.json (reference for new options)"
|
||||||
echo ""
|
echo ""
|
||||||
echo "Enjoy your LED Matrix display!"
|
echo "Enjoy your LED Matrix display!"
|
||||||
|
|||||||
127
fix_assets_permissions.sh
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# LEDMatrix Assets Permissions Fix Script
|
||||||
|
# This script fixes permissions on the assets directory so the application can download and save team logos
|
||||||
|
|
||||||
|
echo "Fixing LEDMatrix assets directory permissions..."
|
||||||
|
|
||||||
|
# Get the real user (not root when running with sudo)
|
||||||
|
REAL_USER=${SUDO_USER:-$USER}
|
||||||
|
# Resolve the home directory of the real user robustly
|
||||||
|
if command -v getent >/dev/null 2>&1; then
|
||||||
|
REAL_HOME=$(getent passwd "$REAL_USER" | cut -d: -f6)
|
||||||
|
else
|
||||||
|
REAL_HOME=$(eval echo ~"$REAL_USER")
|
||||||
|
fi
|
||||||
|
REAL_GROUP=$(id -gn "$REAL_USER")
|
||||||
|
|
||||||
|
# Get the project directory
|
||||||
|
PROJECT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
ASSETS_DIR="$PROJECT_DIR/assets"
|
||||||
|
|
||||||
|
echo "Project directory: $PROJECT_DIR"
|
||||||
|
echo "Assets directory: $ASSETS_DIR"
|
||||||
|
echo "Real user: $REAL_USER"
|
||||||
|
echo "Real group: $REAL_GROUP"
|
||||||
|
|
||||||
|
# Check if assets directory exists
|
||||||
|
if [ ! -d "$ASSETS_DIR" ]; then
|
||||||
|
echo "Error: Assets directory does not exist at $ASSETS_DIR"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Fixing permissions for assets directory and subdirectories..."
|
||||||
|
|
||||||
|
# Set ownership of the entire assets directory to the real user
|
||||||
|
echo "Setting ownership of assets directory..."
|
||||||
|
if sudo chown -R "$REAL_USER:$REAL_GROUP" "$ASSETS_DIR"; then
|
||||||
|
echo "✓ Set assets directory ownership to $REAL_USER:$REAL_GROUP"
|
||||||
|
else
|
||||||
|
echo "✗ Failed to set assets directory ownership"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Set permissions to allow read/write for owner and group, read for others
|
||||||
|
echo "Setting permissions for assets directory..."
|
||||||
|
if sudo chmod -R 775 "$ASSETS_DIR"; then
|
||||||
|
echo "✓ Set assets directory permissions to 775"
|
||||||
|
else
|
||||||
|
echo "✗ Failed to set assets directory permissions"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Specifically ensure the sports logos directories are writable
|
||||||
|
SPORTS_DIRS=(
|
||||||
|
"sports/ncaa_fbs_logos"
|
||||||
|
"sports/nfl_logos"
|
||||||
|
"sports/nba_logos"
|
||||||
|
"sports/nhl_logos"
|
||||||
|
"sports/mlb_logos"
|
||||||
|
"sports/milb_logos"
|
||||||
|
"sports/soccer_logos"
|
||||||
|
)
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Ensuring sports logo directories are writable..."
|
||||||
|
|
||||||
|
for SPORTS_DIR in "${SPORTS_DIRS[@]}"; do
|
||||||
|
FULL_PATH="$ASSETS_DIR/$SPORTS_DIR"
|
||||||
|
echo ""
|
||||||
|
echo "Checking directory: $FULL_PATH"
|
||||||
|
|
||||||
|
if [ -d "$FULL_PATH" ]; then
|
||||||
|
echo " - Directory exists"
|
||||||
|
echo " - Current permissions:"
|
||||||
|
ls -ld "$FULL_PATH"
|
||||||
|
|
||||||
|
# Ensure the directory is writable
|
||||||
|
sudo chmod 775 "$FULL_PATH"
|
||||||
|
sudo chown "$REAL_USER:$REAL_GROUP" "$FULL_PATH"
|
||||||
|
|
||||||
|
echo " - Updated permissions:"
|
||||||
|
ls -ld "$FULL_PATH"
|
||||||
|
|
||||||
|
# Test write access
|
||||||
|
echo " - Testing write access as $REAL_USER..."
|
||||||
|
if sudo -u "$REAL_USER" test -w "$FULL_PATH"; then
|
||||||
|
echo " ✓ $FULL_PATH is writable by $REAL_USER"
|
||||||
|
else
|
||||||
|
echo " ✗ $FULL_PATH is not writable by $REAL_USER"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo " - Directory does not exist, creating it..."
|
||||||
|
sudo mkdir -p "$FULL_PATH"
|
||||||
|
sudo chown "$REAL_USER:$REAL_GROUP" "$FULL_PATH"
|
||||||
|
sudo chmod 775 "$FULL_PATH"
|
||||||
|
echo " - Created directory with proper permissions"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Testing write access to ncaa_fbs_logos directory specifically..."
|
||||||
|
NCAA_DIR="$ASSETS_DIR/sports/ncaa_fbs_logos"
|
||||||
|
if [ -d "$NCAA_DIR" ]; then
|
||||||
|
# Create a test file to verify write access
|
||||||
|
TEST_FILE="$NCAA_DIR/.permission_test"
|
||||||
|
if sudo -u "$REAL_USER" touch "$TEST_FILE" 2>/dev/null; then
|
||||||
|
echo "✓ Successfully created test file in ncaa_fbs_logos directory"
|
||||||
|
sudo -u "$REAL_USER" rm -f "$TEST_FILE"
|
||||||
|
echo "✓ Successfully removed test file"
|
||||||
|
else
|
||||||
|
echo "✗ Failed to create test file in ncaa_fbs_logos directory"
|
||||||
|
echo " This indicates the permission fix did not work properly"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "✗ ncaa_fbs_logos directory does not exist"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Assets permissions fix completed!"
|
||||||
|
echo ""
|
||||||
|
echo "The application should now be able to download and save team logos."
|
||||||
|
echo "If you still see permission errors, check which user is running the LEDMatrix service"
|
||||||
|
echo "and ensure it matches the owner above ($REAL_USER)."
|
||||||
|
echo ""
|
||||||
|
echo "You may need to restart the LEDMatrix service for the changes to take effect:"
|
||||||
|
echo " sudo systemctl restart ledmatrix.service"
|
||||||
43
migrate_config.sh
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
#!/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!"
|
||||||
1230
milb_main.py
@@ -101,7 +101,7 @@ def show_status():
|
|||||||
|
|
||||||
def main():
|
def main():
|
||||||
if len(sys.argv) < 2:
|
if len(sys.argv) < 2:
|
||||||
print("Usage: python3 enable_news_manager.py [enable|disable|status]")
|
print("Usage: python3 scripts/enable_news_manager.py [enable|disable|status]")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
command = sys.argv[1].lower()
|
command = sys.argv[1].lower()
|
||||||
@@ -7,6 +7,7 @@ class ConfigManager:
|
|||||||
# Use current working directory as base
|
# Use current working directory as base
|
||||||
self.config_path = config_path or "config/config.json"
|
self.config_path = config_path or "config/config.json"
|
||||||
self.secrets_path = secrets_path or "config/config_secrets.json"
|
self.secrets_path = secrets_path or "config/config_secrets.json"
|
||||||
|
self.template_path = "config/config.template.json"
|
||||||
self.config: Dict[str, Any] = {}
|
self.config: Dict[str, Any] = {}
|
||||||
|
|
||||||
def get_config_path(self) -> str:
|
def get_config_path(self) -> str:
|
||||||
@@ -18,11 +19,18 @@ class ConfigManager:
|
|||||||
def load_config(self) -> Dict[str, Any]:
|
def load_config(self) -> Dict[str, Any]:
|
||||||
"""Load configuration from JSON files."""
|
"""Load configuration from JSON files."""
|
||||||
try:
|
try:
|
||||||
|
# Check if config file exists, if not create from template
|
||||||
|
if not os.path.exists(self.config_path):
|
||||||
|
self._create_config_from_template()
|
||||||
|
|
||||||
# Load main config
|
# Load main config
|
||||||
print(f"Attempting to load config from: {os.path.abspath(self.config_path)}")
|
print(f"Attempting to load config from: {os.path.abspath(self.config_path)}")
|
||||||
with open(self.config_path, 'r') as f:
|
with open(self.config_path, 'r') as f:
|
||||||
self.config = json.load(f)
|
self.config = json.load(f)
|
||||||
|
|
||||||
|
# Migrate config to add any new items from template
|
||||||
|
self._migrate_config()
|
||||||
|
|
||||||
# Load and merge secrets if they exist (be permissive on errors)
|
# Load and merge secrets if they exist (be permissive on errors)
|
||||||
if os.path.exists(self.secrets_path):
|
if os.path.exists(self.secrets_path):
|
||||||
try:
|
try:
|
||||||
@@ -118,6 +126,85 @@ class ConfigManager:
|
|||||||
else:
|
else:
|
||||||
target[key] = value
|
target[key] = value
|
||||||
|
|
||||||
|
def _create_config_from_template(self) -> None:
|
||||||
|
"""Create config.json from template if it doesn't exist."""
|
||||||
|
if not os.path.exists(self.template_path):
|
||||||
|
raise FileNotFoundError(f"Template file not found at {os.path.abspath(self.template_path)}")
|
||||||
|
|
||||||
|
print(f"Creating config.json from template at {os.path.abspath(self.template_path)}")
|
||||||
|
|
||||||
|
# Ensure config directory exists
|
||||||
|
os.makedirs(os.path.dirname(self.config_path), exist_ok=True)
|
||||||
|
|
||||||
|
# Copy template to config
|
||||||
|
with open(self.template_path, 'r') as template_file:
|
||||||
|
template_data = json.load(template_file)
|
||||||
|
|
||||||
|
with open(self.config_path, 'w') as config_file:
|
||||||
|
json.dump(template_data, config_file, indent=4)
|
||||||
|
|
||||||
|
print(f"Created config.json from template at {os.path.abspath(self.config_path)}")
|
||||||
|
|
||||||
|
def _migrate_config(self) -> None:
|
||||||
|
"""Migrate config to add new items from template with defaults."""
|
||||||
|
if not os.path.exists(self.template_path):
|
||||||
|
print(f"Template file not found at {os.path.abspath(self.template_path)}, skipping migration")
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
with open(self.template_path, 'r') as f:
|
||||||
|
template_config = json.load(f)
|
||||||
|
|
||||||
|
# Check if migration is needed
|
||||||
|
if self._config_needs_migration(self.config, template_config):
|
||||||
|
print("Config migration needed - adding new configuration items with defaults")
|
||||||
|
|
||||||
|
# Create backup of current config
|
||||||
|
backup_path = f"{self.config_path}.backup"
|
||||||
|
with open(backup_path, 'w') as backup_file:
|
||||||
|
json.dump(self.config, backup_file, indent=4)
|
||||||
|
print(f"Created backup of current config at {os.path.abspath(backup_path)}")
|
||||||
|
|
||||||
|
# Merge template defaults into current config
|
||||||
|
self._merge_template_defaults(self.config, template_config)
|
||||||
|
|
||||||
|
# Save migrated config
|
||||||
|
with open(self.config_path, 'w') as f:
|
||||||
|
json.dump(self.config, f, indent=4)
|
||||||
|
|
||||||
|
print(f"Config migration completed and saved to {os.path.abspath(self.config_path)}")
|
||||||
|
else:
|
||||||
|
print("Config is up to date, no migration needed")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error during config migration: {e}")
|
||||||
|
# Don't raise - continue with current config
|
||||||
|
|
||||||
|
def _config_needs_migration(self, current_config: Dict[str, Any], template_config: Dict[str, Any]) -> bool:
|
||||||
|
"""Check if config needs migration by comparing with template."""
|
||||||
|
return self._has_new_keys(current_config, template_config)
|
||||||
|
|
||||||
|
def _has_new_keys(self, current: Dict[str, Any], template: Dict[str, Any]) -> bool:
|
||||||
|
"""Recursively check if template has keys not in current config."""
|
||||||
|
for key, value in template.items():
|
||||||
|
if key not in current:
|
||||||
|
return True
|
||||||
|
if isinstance(value, dict) and isinstance(current[key], dict):
|
||||||
|
if self._has_new_keys(current[key], value):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def _merge_template_defaults(self, current: Dict[str, Any], template: Dict[str, Any]) -> None:
|
||||||
|
"""Recursively merge template defaults into current config."""
|
||||||
|
for key, value in template.items():
|
||||||
|
if key not in current:
|
||||||
|
# Add new key with template value
|
||||||
|
current[key] = value
|
||||||
|
print(f"Added new config key: {key}")
|
||||||
|
elif isinstance(value, dict) and isinstance(current[key], dict):
|
||||||
|
# Recursively merge nested dictionaries
|
||||||
|
self._merge_template_defaults(current[key], value)
|
||||||
|
|
||||||
def get_timezone(self) -> str:
|
def get_timezone(self) -> str:
|
||||||
"""Get the configured timezone."""
|
"""Get the configured timezone."""
|
||||||
return self.config.get('timezone', 'UTC')
|
return self.config.get('timezone', 'UTC')
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 65 KiB |
|
Before Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 54 KiB |
|
Before Width: | Height: | Size: 109 KiB |
|
Before Width: | Height: | Size: 42 KiB |
|
Before Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 53 KiB |
|
Before Width: | Height: | Size: 105 KiB |
|
Before Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 53 KiB |
|
Before Width: | Height: | Size: 49 KiB |
|
Before Width: | Height: | Size: 84 KiB |
|
Before Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 47 KiB |
|
Before Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 42 KiB |
|
Before Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 73 KiB |
|
Before Width: | Height: | Size: 56 KiB |
|
Before Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 66 KiB |
|
Before Width: | Height: | Size: 61 KiB |
|
Before Width: | Height: | Size: 40 KiB |
|
Before Width: | Height: | Size: 81 KiB |
|
Before Width: | Height: | Size: 96 KiB |
|
Before Width: | Height: | Size: 6.3 KiB |
|
Before Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 45 KiB |
|
Before Width: | Height: | Size: 78 KiB |
|
Before Width: | Height: | Size: 47 KiB |
|
Before Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 47 KiB |
|
Before Width: | Height: | Size: 56 KiB |
|
Before Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 5.0 KiB |
|
Before Width: | Height: | Size: 40 KiB |
|
Before Width: | Height: | Size: 23 KiB |
@@ -206,13 +206,13 @@ This single script handles everything you need for a new installation.
|
|||||||
|
|
||||||
### **Dependency Installation Scripts**
|
### **Dependency Installation Scripts**
|
||||||
|
|
||||||
#### `install_dependencies_apt.py`
|
#### `scripts/install_dependencies_apt.py`
|
||||||
- **When to use**: When you want to install packages via apt first, then pip
|
- **When to use**: When you want to install packages via apt first, then pip
|
||||||
- **What it does**:
|
- **What it does**:
|
||||||
- Tries to install packages via apt (system packages)
|
- Tries to install packages via apt (system packages)
|
||||||
- Falls back to pip with `--break-system-packages`
|
- Falls back to pip with `--break-system-packages`
|
||||||
- Handles externally managed Python environments
|
- Handles externally managed Python environments
|
||||||
- **Usage**: `sudo python3 install_dependencies_apt.py`
|
- **Usage**: `sudo python3 scripts/install_dependencies_apt.py`
|
||||||
|
|
||||||
#### `start_web_v2.py`
|
#### `start_web_v2.py`
|
||||||
- **When to use**: Manual web interface startup
|
- **When to use**: Manual web interface startup
|
||||||
@@ -252,7 +252,7 @@ sudo ./first_time_install.sh
|
|||||||
### **Scenario 2: Adding Web Interface to Existing Installation**
|
### **Scenario 2: Adding Web Interface to Existing Installation**
|
||||||
```bash
|
```bash
|
||||||
# Install web interface dependencies
|
# Install web interface dependencies
|
||||||
sudo python3 install_dependencies_apt.py
|
sudo python3 scripts/install_dependencies_apt.py
|
||||||
|
|
||||||
# Fix permissions
|
# Fix permissions
|
||||||
./fix_web_permissions.sh
|
./fix_web_permissions.sh
|
||||||
@@ -332,7 +332,7 @@ http://your-pi-ip:5001
|
|||||||
2. Log out and back in
|
2. Log out and back in
|
||||||
|
|
||||||
### **Dependency Installation Errors**
|
### **Dependency Installation Errors**
|
||||||
1. Run: `sudo python3 install_dependencies_apt.py`
|
1. Run: `sudo python3 scripts/install_dependencies_apt.py`
|
||||||
|
|
||||||
## Summary
|
## Summary
|
||||||
|
|
||||||
|
|||||||
@@ -50,17 +50,17 @@ Other: https://www.coveringthecorner.com/rss/current.xml
|
|||||||
|
|
||||||
### Command Line Management
|
### Command Line Management
|
||||||
|
|
||||||
Use the `enable_news_manager.py` script to manage the news manager:
|
Use the `scripts/enable_news_manager.py` script to manage the news manager:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Check current status
|
# Check current status
|
||||||
python3 enable_news_manager.py status
|
python3 scripts/enable_news_manager.py status
|
||||||
|
|
||||||
# Enable news manager
|
# Enable news manager
|
||||||
python3 enable_news_manager.py enable
|
python3 scripts/enable_news_manager.py enable
|
||||||
|
|
||||||
# Disable news manager
|
# Disable news manager
|
||||||
python3 enable_news_manager.py disable
|
python3 scripts/enable_news_manager.py disable
|
||||||
```
|
```
|
||||||
|
|
||||||
### Web Interface
|
### Web Interface
|
||||||
|
|||||||