-
-
Notifications
You must be signed in to change notification settings - Fork 29
Expand file tree
/
Copy pathstart_web_conditionally.py
More file actions
142 lines (124 loc) · 6.96 KB
/
Copy pathstart_web_conditionally.py
File metadata and controls
142 lines (124 loc) · 6.96 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
import json
import os
import sys
import subprocess
from pathlib import Path
# Get project root directory (parent of scripts/utils/)
PROJECT_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
CONFIG_FILE = os.path.join(PROJECT_DIR, 'config', 'config.json')
WEB_INTERFACE_SCRIPT = os.path.join(PROJECT_DIR, 'web_interface', 'start.py')
# Marker file created by first_time_install.sh to indicate dependencies are installed
DEPS_MARKER = os.path.join(PROJECT_DIR, '.web_deps_installed')
def dependencies_installed():
"""Check if dependencies were installed during first-time setup."""
return os.path.exists(DEPS_MARKER)
def install_dependencies():
"""Install required dependencies using system Python."""
print("Installing dependencies...")
try:
requirements_file = os.path.join(PROJECT_DIR, 'web_interface', 'requirements.txt')
# Use --ignore-installed to handle system packages (like psutil) that can't be uninstalled
# This allows pip to install even if a system package version conflicts
result = subprocess.run([
sys.executable, '-m', 'pip', 'install', '--break-system-packages', '--ignore-installed', '-r', requirements_file
], capture_output=True, text=True)
if result.returncode != 0:
# Check if the error is just about psutil version conflict
if 'psutil' in result.stderr.lower() and ('uninstall' in result.stderr.lower() or 'cannot uninstall' in result.stderr.lower()):
print("Warning: psutil version conflict detected (system package vs requirements).")
print("Attempting to install other dependencies without psutil...")
# Try installing without psutil
with open(requirements_file, 'r') as f:
lines = f.readlines()
# Filter out psutil line
filtered_lines = [line for line in lines if 'psutil' not in line.lower()]
temp_reqs = os.path.join(PROJECT_DIR, 'web_interface', 'requirements_temp.txt')
with open(temp_reqs, 'w') as f:
f.writelines(filtered_lines)
try:
subprocess.check_call([
sys.executable, '-m', 'pip', 'install', '--break-system-packages', '--ignore-installed', '-r', temp_reqs
])
print("Dependencies installed successfully (psutil skipped - using system version)")
finally:
if os.path.exists(temp_reqs):
os.remove(temp_reqs)
else:
# Re-raise the error if it's not about psutil
print(f"Failed to install dependencies: {result.stderr}")
return False
else:
print("Dependencies installed successfully")
# Install rgbmatrix module from local source (optional - not required for web interface)
print("Installing rgbmatrix module (optional)...")
rgbmatrix_path = Path(PROJECT_DIR) / 'rpi-rgb-led-matrix-master' / 'bindings' / 'python'
if rgbmatrix_path.exists():
try:
subprocess.check_call([
sys.executable, '-m', 'pip', 'install', '--break-system-packages', '-e', str(rgbmatrix_path)
], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
print("rgbmatrix module installed successfully")
except subprocess.CalledProcessError:
print("Warning: rgbmatrix module installation failed (not required for web interface, continuing...)")
else:
print("rgbmatrix module path not found (not required for web interface, continuing...)")
return True
except subprocess.CalledProcessError as e:
print(f"Failed to install dependencies: {e}")
return False
#: String spellings that turn autostart OFF. Anything else -- including the key
#: being absent entirely -- leaves it on.
DISABLED_STRINGS = ("off", "false", "no", "0")
def autostart_enabled(config_data):
"""Whether to bring the web interface up. Defaults to True.
config.template.json and first_time_install.sh both ship
``web_display_autostart`` as true, so a config that lacks the key is an
older or hand-edited one rather than a request to stay down. Defaulting to
False meant any such config silently got no web interface -- and because
the "not starting" path exits 0, systemd reported the unit as successfully
started while nothing was listening. Only an explicit false/off disables it.
"""
value = config_data.get("web_display_autostart", True)
if isinstance(value, str):
return value.strip().lower() not in DISABLED_STRINGS
return bool(value)
def main():
try:
with open(CONFIG_FILE, 'r') as f:
config_data = json.load(f)
except FileNotFoundError:
# The web interface is how a config gets created and repaired, so a
# missing one is the case where the user needs it most.
print(f"Config file {CONFIG_FILE} not found. Starting the web interface so it can be configured.")
config_data = {}
except (json.JSONDecodeError, OSError) as e:
print(f"Error reading config file {CONFIG_FILE}: {e}. Starting the web interface anyway so the config can be repaired.")
config_data = {}
if autostart_enabled(config_data):
print("Configuration 'web_display_autostart' is enabled. Starting web interface...")
# Only install dependencies if not already done during first-time setup
if not dependencies_installed():
print("First run detected: Installing dependencies...")
if not install_dependencies():
print("Failed to install dependencies. Exiting.")
sys.exit(1)
# Create marker file after successful install
Path(DEPS_MARKER).touch()
print("Dependencies installed and marker file created.")
else:
print("Dependencies already installed (marker file found). Skipping installation.")
try:
# Replace the current process with web_interface.py using system Python
# This is important for systemd to correctly manage the web server process.
# Ensure PYTHONPATH is set correctly if web_interface.py has relative imports to src
# The WorkingDirectory in systemd service should handle this for web_interface.py
print(f"Launching web interface v3: {sys.executable} {WEB_INTERFACE_SCRIPT}")
os.execvp(sys.executable, [sys.executable, WEB_INTERFACE_SCRIPT]) # nosec B606 - both args are fixed constants
except Exception as e:
print(f"Failed to exec web interface: {e}")
sys.exit(1) # Failed to start
else:
print("Configuration 'web_display_autostart' is explicitly disabled. Web interface will not be started.")
sys.exit(0) # Exit gracefully, service considered successful
if __name__ == '__main__':
main()