diff --git a/app/cli.py b/app/cli.py index db0b125..36acc44 100644 --- a/app/cli.py +++ b/app/cli.py @@ -3,6 +3,7 @@ import argparse from . import downloader from . import setup_config +import subprocess def main(): parser = argparse.ArgumentParser(description="Fetchtastic - Meshtastic Firmware and APK Downloader") @@ -33,7 +34,7 @@ def main(): # Run the downloader downloader.main() elif args.command == 'topic': - # Display the NTFY topic + # Display the NTFY topic and prompt to copy to clipboard config = setup_config.load_config() if config and config.get('NTFY_SERVER') and config.get('NTFY_TOPIC'): ntfy_server = config['NTFY_SERVER'].rstrip('/') @@ -41,6 +42,12 @@ def main(): full_url = f"{ntfy_server}/{ntfy_topic}" print(f"Current NTFY topic URL: {full_url}") print(f"Topic name: {ntfy_topic}") + copy_to_clipboard = input("Do you want to copy the topic name to the clipboard? [y/n] (default: yes): ").strip().lower() or 'y' + if copy_to_clipboard == 'y': + copy_to_clipboard_termux(ntfy_topic) + print("Topic name copied to clipboard.") + else: + print("You can copy the topic name from above.") else: print("Notifications are not set up. Run 'fetchtastic setup' to configure notifications.") elif args.command == 'clean': @@ -53,5 +60,11 @@ def main(): else: parser.print_help() +def copy_to_clipboard_termux(text): + try: + subprocess.run(['termux-clipboard-set'], input=text.encode('utf-8'), check=True) + except Exception as e: + print(f"An error occurred while copying to clipboard: {e}") + if __name__ == "__main__": main() diff --git a/app/downloader.py b/app/downloader.py index 9a1975f..99cdf8c 100644 --- a/app/downloader.py +++ b/app/downloader.py @@ -26,6 +26,7 @@ def main(): firmware_versions_to_keep = config.get("FIRMWARE_VERSIONS_TO_KEEP", 2) auto_extract = config.get("AUTO_EXTRACT", False) extract_patterns = config.get("EXTRACT_PATTERNS", []) + wifi_only = config.get("WIFI_ONLY", True) selected_apk_patterns = config.get('SELECTED_APK_ASSETS', []) selected_firmware_patterns = config.get('SELECTED_FIRMWARE_ASSETS', []) @@ -77,7 +78,7 @@ def download_file(url, download_path): adapter = HTTPAdapter(max_retries=retry) session.mount('https://', adapter) session.mount('http://', adapter) - + try: if not os.path.exists(download_path): log_message(f"Downloading {url}") @@ -92,6 +93,17 @@ def download_file(url, download_path): except requests.exceptions.RequestException as e: log_message(f"Error downloading {url}: {e}") + def is_connected_to_wifi(): + try: + result = os.popen('termux-wifi-connectioninfo').read() + if '"connected":true' in result: + return True + else: + return False + except Exception as e: + log_message(f"Error checking Wi-Fi connection: {e}") + return False + # Updated extract_files function def extract_files(zip_path, extract_dir, patterns): try: @@ -158,7 +170,12 @@ def check_and_download(releases, latest_release_file, release_type, download_dir else: # Proceed to download this version os.makedirs(release_dir, exist_ok=True) - log_message(f"Downloading new version: {release_tag}") + log_message(f"New {release_type} version {release_tag} is available.") + # Check Wi-Fi connection before downloading + if wifi_only and not is_connected_to_wifi(): + log_message("Not connected to Wi-Fi. Skipping download.") + send_ntfy_notification(f"New {release_type} version {release_tag} is available, but not connected to Wi-Fi. Download will proceed when connected.") + continue # Skip downloading this release for asset in release['assets']: file_name = asset['name'] # Modify the matching logic here @@ -252,7 +269,7 @@ def check_and_download(releases, latest_release_file, release_type, download_dir else: if latest_firmware_releases or latest_android_releases: message = ( - f"All Firmware and Android APK versions are up to date.\n" + f"No new downloads. All Firmware and Android APK versions are up to date or waiting for Wi-Fi connection.\n" f"Latest Firmware releases: {', '.join(release['tag_name'] for release in latest_firmware_releases[:versions_to_download])}\n" f"Latest Android APK releases: {', '.join(release['tag_name'] for release in latest_android_releases[:versions_to_download])}\n" f"{datetime.now()}" diff --git a/app/setup_config.py b/app/setup_config.py index d96476d..7cc23f7 100644 --- a/app/setup_config.py +++ b/app/setup_config.py @@ -5,7 +5,7 @@ import subprocess import random import string -import shutil # Added for shutil.which() +import shutil from . import menu_apk from . import menu_firmware from . import downloader # Import downloader to perform first run @@ -34,28 +34,23 @@ def get_downloads_dir(): def config_exists(): return os.path.exists(CONFIG_FILE) -def check_storage_setup(): - # Check if the Termux storage directory and Downloads are set up and writable - storage_dir = os.path.expanduser("~/storage") - storage_downloads = os.path.expanduser("~/storage/downloads") - - # If ~/storage or ~/storage/downloads doesn't exist or isn't writable, exit - if not os.path.exists(storage_dir) or not os.path.exists(storage_downloads) or not os.access(storage_downloads, os.W_OK): - print("Storage access is required to continue.") - print("Please run 'termux-setup-storage' and restart Termux before running this setup again.") - exit() # Exit the script as storage is not set up - print("Storage access has been successfully verified.") - return True +def is_termux(): + return 'com.termux' in os.environ.get('PREFIX', '') def run_setup(): - # First, check if Termux storage has been set up - if is_termux() and not check_storage_setup(): - print("Setup cannot continue without proper storage access.") - return - - # The rest of your setup process continues here print("Running Fetchtastic Setup...") + # Install required Termux packages first + if is_termux(): + install_termux_packages() + # Run termux-setup-storage + setup_storage() + # After setting up storage, inform the user and exit + print("Termux storage has been set up, and required packages have been installed.") + print("Please restart Termux and run 'fetchtastic setup' again to continue.") + exit() + + # The rest of your setup process continues here if not os.path.exists(DEFAULT_CONFIG_DIR): os.makedirs(DEFAULT_CONFIG_DIR) @@ -121,6 +116,10 @@ def run_setup(): else: config['AUTO_EXTRACT'] = False + # Ask if the user wants to only download when connected to Wi-Fi + wifi_only = input("Do you want to only download when connected to Wi-Fi? [y/n] (default: yes): ").strip().lower() or 'y' + config['WIFI_ONLY'] = True if wifi_only == 'y' else False + # Set the download directory to the same as the config directory download_dir = DEFAULT_CONFIG_DIR config['DOWNLOAD_DIR'] = download_dir @@ -182,21 +181,42 @@ def run_setup(): else: print("Setup complete. Run 'fetchtastic download' to start downloading.") -def is_termux(): - return 'com.termux' in os.environ.get('PREFIX', '') - def copy_to_clipboard_termux(text): try: subprocess.run(['termux-clipboard-set'], input=text.encode('utf-8'), check=True) except Exception as e: print(f"An error occurred while copying to clipboard: {e}") +def install_termux_packages(): + # Install termux-api and termux-services if they are not installed + packages_to_install = [] + # Check for termux-api + if shutil.which('termux-battery-status') is None: + packages_to_install.append('termux-api') + # Check for termux-services + if shutil.which('sv-enable') is None: + packages_to_install.append('termux-services') + if packages_to_install: + print("Installing required Termux packages...") + subprocess.run(['pkg', 'install'] + packages_to_install + ['-y'], check=True) + print("Required Termux packages installed.") + else: + print("All required Termux packages are already installed.") + +def setup_storage(): + # Run termux-setup-storage + print("Setting up Termux storage access...") + subprocess.run(['termux-setup-storage'], check=True) + def install_crond(): try: crond_path = shutil.which('crond') if crond_path is None: print("Installing crond...") - subprocess.run(['pkg', 'install', 'termux-services', '-y'], check=True) + # Ensure termux-services is installed + if shutil.which('sv-enable') is None: + subprocess.run(['pkg', 'install', 'termux-services', '-y'], check=True) + # Enable crond service subprocess.run(['sv-enable', 'crond'], check=True) print("crond installed and started.") else: @@ -221,35 +241,21 @@ def setup_cron_job(): if keep_cron == 'n': # Remove existing fetchtastic cron jobs new_cron = '\n'.join([line for line in existing_cron.split('\n') if 'fetchtastic download' not in line]) + # Add new cron job + new_cron += f"\n0 3 * * * fetchtastic download\n" # Update crontab process = subprocess.Popen(['crontab', '-'], stdin=subprocess.PIPE, text=True) process.communicate(input=new_cron) - print("Existing Fetchtastic cron job removed.") - # Ask if they want to add a new cron job - add_cron = input("Would you like to schedule Fetchtastic to run daily at 3 AM? [y/n] (default: yes): ").strip().lower() or 'y' - if add_cron == 'y': - # Add new cron job - new_cron += f"\n0 3 * * * fetchtastic download\n" - # Update crontab - process = subprocess.Popen(['crontab', '-'], stdin=subprocess.PIPE, text=True) - process.communicate(input=new_cron) - print("New cron job added.") - else: - print("Skipping cron job installation.") + print("Cron job updated.") else: print("Keeping existing crontab entry.") else: - # No existing fetchtastic cron job - add_cron = input("Would you like to schedule Fetchtastic to run daily at 3 AM? [y/n] (default: yes): ").strip().lower() or 'y' - if add_cron == 'y': - # Add new cron job - new_cron = existing_cron.strip() + f"\n0 3 * * * fetchtastic download\n" - # Update crontab - process = subprocess.Popen(['crontab', '-'], stdin=subprocess.PIPE, text=True) - process.communicate(input=new_cron) - print("Cron job added to run Fetchtastic daily at 3 AM.") - else: - print("Skipping cron job installation.") + # Add new cron job + new_cron = existing_cron.strip() + f"\n0 3 * * * fetchtastic download\n" + # Update crontab + process = subprocess.Popen(['crontab', '-'], stdin=subprocess.PIPE, text=True) + process.communicate(input=new_cron) + print("Cron job added to run Fetchtastic daily at 3 AM.") except Exception as e: print(f"An error occurred while setting up the cron job: {e}") diff --git a/setup.cfg b/setup.cfg index 533a159..ad50f26 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = fetchtastic -version = 0.1.5 +version = 0.1.6 author = Jeremiah K author_email = jeremiahk@gmx.com description = Meshtastic Firmware and APK Downloader