''' __ __ _ | \/ | __ _ ___| |_ ___ _ __ | |\/| |/ _` / __| __/ _ \ '__| | | | | (_| \__ \ || __/ | |_| |_|\__,_|___/\__\___|_| Master Python script that will trigger other scripts at every chosen time pace This script is triggered as a systemd service used as an alternative to cronjobs Attention: to do -> prevent SARA R4 Script to run again if it's taking more than 60 secs to finish (using a lock file ??) First time: need to create the service file --> sudo nano /etc/systemd/system/master_nebuleair.service <-- ⬇️ [Unit] Description=Master manager for the Python loop scripts After=network.target [Service] ExecStart=/usr/bin/python3 /var/www/moduleair_pro_4g/master.py Restart=always User=root WorkingDirectory=/var/www/moduleair_pro_4g StandardOutput=append:/var/www/moduleair_pro_4g/logs/master.log StandardError=append:/var/www/moduleair_pro_4g/logs/master_errors.log [Install] WantedBy=multi-user.target ⬆️ Reload systemd (first time after creating the service): sudo systemctl daemon-reload Enable (once), start (once and after stopping) and restart (after modification)systemd: sudo systemctl enable master_nebuleair.service sudo systemctl start master_nebuleair.service sudo systemctl restart master_nebuleair.service Check the service status: sudo systemctl status master_nebuleair.service Specific scripts can be disabled with config.json Exemple: stop gathering data from NPM Exemple: stop sending data with SARA R4 ''' import time import threading import subprocess import json import os # Base directory where scripts are stored SCRIPT_DIR = "/var/www/moduleair_pro_4g/" CONFIG_FILE = "/var/www/moduleair_pro_4g/config.json" def load_config(): """Load the configuration file to determine which scripts to run.""" with open(CONFIG_FILE, "r") as f: return json.load(f) def run_script(script_name, interval, delay=0): """Run a PYTHON script in a synchronized loop with an optional start delay.""" script_path = os.path.join(SCRIPT_DIR, script_name) # Build full path next_run = time.monotonic() + delay # Apply the initial delay while True: config = load_config() if config.get(script_name, True): # Default to True if not found subprocess.run(["python3", script_path]) # Wait until the next exact interval next_run += interval sleep_time = max(0, next_run - time.monotonic()) # Prevent negative sleep times time.sleep(sleep_time) # ✅ **Scripts that should start immediately** IMMEDIATE_SCRIPTS = [ ("RTC/save_to_db.py", 1, 0), ("NPM/get_data_modbus_v3.py", 10, 0), ("envea/read_value_v2.py", 10, 0), ("MH-Z19/write_data.py", 10, 0), ("loop/SARA_send_data_v2.py", 60, 1), ("BME280/get_data_v2.py", 120, 0), ("sqlite/flush_old_data.py", 86400, 0) ] # ✅ **The compiled binary that must wait for welcome_screen** WAIT_BINARY = "/var/www/moduleair_pro_4g/matrix/test_forms" # Adjust path if needed # 🚀 **Start all scripts that can run immediately** for script_name, interval, delay in IMMEDIATE_SCRIPTS: thread = threading.Thread(target=run_script, args=(script_name, interval, delay), daemon=True) thread.start() # 🚀 **Run welcome_screen in parallel** print("🚀 Running welcome screen...") welcome_process = subprocess.Popen(["sudo", "/var/www/moduleair_pro_4g/matrix/welcome_screen"]) print("✅ Welcome screen started in parallel with other scripts.") # 🕒 **Wait for welcome_screen to finish before running WAIT_BINARY** welcome_process.wait() print("✅ Welcome screen finished. Starting another_screen...") # 🚀 **Now run the compiled binary `another_screen`** subprocess.run(["sudo", WAIT_BINARY]) print("✅ another_screen execution completed.") # Keep the main script running while True: time.sleep(1)