diff --git a/extra_scripts/post.py b/extra_scripts/post.py new file mode 100644 index 0000000..34615da --- /dev/null +++ b/extra_scripts/post.py @@ -0,0 +1,91 @@ +#!/usr/bin/env python3 +""" +Post-upload script for PlatformIO + +This script runs after successful firmware upload and performs the following actions: +1. Renames secrets.json to secrets.json.injected to indicate successful injection +2. Preserves previous injected files with timestamps +3. Provides clear feedback about the secrets injection status + +Usage: +- Automatically runs after: pio run -e --target upload +- Helps track which secrets have been injected into firmware +- Prevents accidental reuse of the same secrets file + +To reuse secrets: cp secrets.json.injected secrets.json +""" + +import os +import json +import shutil +from datetime import datetime + +Import("env") + + +def rename_secrets_after_upload(*args, **kwargs): + """ + Post-upload action to rename secrets.json to secrets.json.injected + This indicates that the secrets have been successfully injected into the firmware. + Adds a syntax error comment to force users to read warnings when reusing. + """ + secrets_file = "secrets.json" + injected_file = "secrets.json.injected" + + if os.path.exists(secrets_file): + try: + # Add timestamp to the injected file for tracking + timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") + timestamped_file = f"secrets.json.injected.{timestamp}" + + # If an injected file already exists, rename it with timestamp + if os.path.exists(injected_file): + print(f"Previous {injected_file} found, renaming to {timestamped_file}") + shutil.move(injected_file, timestamped_file) + + # Read the original secrets file + with open(secrets_file, 'r') as f: + original_content = f.read() + + # Create content with intentional JSON syntax error + # Add a trailing comma and comment that breaks JSON parsing + syntax_error_content = f'''{{ + "WARNING_REMOVE_THIS_LINE_TO_USE": "If you modified secrets, ERASE DEVICE FIRST: pio run -e -t erase", + // INTENTIONAL SYNTAX ERROR: Remove this comment line and the line above to use +{original_content[1:-1] if len(original_content) >= 2 else ''}, + "INJECTED_TIMESTAMP": "{timestamp}", +}}''' + + # Write the content with syntax error to injected file + with open(injected_file, 'w') as f: + f.write(syntax_error_content) + + # Remove the original file + os.remove(secrets_file) + + print(f"✓ Successfully created {injected_file} with deployment tracking") + print(f" This indicates secrets have been injected into the firmware.") + print(f" To reuse: copy {injected_file} to {secrets_file} and remove the warning line") + + except Exception as e: + print(f"Warning: Failed to process {secrets_file}: {e}") + # Fallback to simple rename if JSON processing fails + try: + shutil.move(secrets_file, injected_file) + print(f"✓ Fallback: renamed {secrets_file} to {injected_file}") + except Exception as e2: + print(f"Warning: Fallback rename also failed: {e2}") + else: + print(f"No {secrets_file} file found - nothing to rename") + + +def main(): + """ + Main function that sets up the post-upload action + """ + # Add post-upload action to rename secrets file + env.AddPostAction("upload", rename_secrets_after_upload) + + +# Execute main function +main() diff --git a/extra_scripts/pre.py b/extra_scripts/pre.py index 6a9d4de..0d37d28 100644 --- a/extra_scripts/pre.py +++ b/extra_scripts/pre.py @@ -6,6 +6,83 @@ Import("env") +def load_secrets_config(): + """ + Load secrets configuration based on available files. + Handles three scenarios: + 1. secrets.json exists - use it (fresh deployment) + 2. secrets.json.injected exists but secrets.json doesn't - inform user + 3. Neither exists - use placeholder values + """ + secrets_file = "secrets.json" + injected_file = "secrets.json.injected" + + secrets_exist = os.path.exists(secrets_file) + injected_exist = os.path.exists(injected_file) + + if secrets_exist: + # Scenario 1: secrets.json exists - use it for fresh deployment + print("=" * 60) + print("📄 Found secrets.json - Loading configuration for deployment") + if injected_exist: + print("âš ī¸ WARNING: Both secrets.json and secrets.json.injected exist!") + print(" This suggests you may be re-deploying with new secrets.") + print(" If you modified secrets, you MUST erase the device first:") + print(" pio run -e -t erase") + print("=" * 60) + + try: + with open(secrets_file, "r") as f: + json_config = json.load(f) + return { + "wifi_ssid": json_config.get("WIFI_SSID", ""), + "wifi_password": json_config.get("WIFI_PASSWORD", ""), + "remote_url": json_config.get("REMOTE_URL", ""), + "refresh_interval_seconds": json_config.get("REFRESH_INTERVAL_SECONDS", 10), + "default_brightness": json_config.get("DEFAULT_BRIGHTNESS", 10), + "source": "secrets.json" + } + except (json.JSONDecodeError, IOError) as e: + print(f"❌ Error reading secrets.json: {e}") + print(" Build failed - cannot proceed without valid secrets configuration.") + exit(1) + + elif injected_exist: + # Scenario 2: Only secrets.json.injected exists - use placeholders + print("=" * 60) + print("📋 Found secrets.json.injected but no secrets.json") + print(" The injected file indicates secrets were previously deployed.") + print("") + print(" To deploy with NEW secrets:") + print(" 1. Copy: cp secrets.json.injected secrets.json") + print(" 2. Edit secrets.json and fix any syntax errors") + print(" 3. If you modified secrets, run: pio run -e -t erase") + print(" 4. Run: pio run -e --target upload") + print("=" * 60) + + else: + # Scenario 3: Neither file exists - first time setup + print("=" * 60) + print("🆕 No secrets files found - First time setup") + print(" Using PLACEHOLDER values for firmware compilation.") + print("") + print(" To deploy with real secrets:") + print(" 1. Copy: cp secrets.json.example secrets.json") + print(" 2. Edit secrets.json with your actual values") + print(" 3. Run: pio run -e --target upload") + print("=" * 60) + + # Return placeholder values for scenarios 2 and 3 + return { + "wifi_ssid": "XplaceholderWIFISSID____________", + "wifi_password": "XplaceholderWIFIPASSWORD________________________________________", + "remote_url": "XplaceholderREMOTEURL___________________________________________________________________________________________________________", + "refresh_interval_seconds": 10, + "default_brightness": 30, + "source": "secrets.json.injected" + } + + def main() -> None: # copy libwebp's library.json to the lib directory env.Execute(Copy("$PROJECT_LIBDEPS_DIR/$PIOENV/libwebp/library.json", "$PROJECT_DIR/lib/webp/library.json")) @@ -15,44 +92,31 @@ def main() -> None: print(f"Deleting existing {sdkconfig_path} to force regeneration...") os.remove(sdkconfig_path) - # if secrets.h file exists - if os.path.exists("secrets.json"): - # read secrets.h file - with open("secrets.json", "r") as f: - json_config = json.load(f) - - wifi_ssid = json_config.get("WIFI_SSID", "") - wifi_password = json_config.get("WIFI_PASSWORD", "") - remote_url = json_config.get("REMOTE_URL", "") - refresh_interval_seconds = json_config.get( - "REFRESH_INTERVAL_SECONDS", 10 - ) - default_brightness = json_config.get("DEFAULT_BRIGHTNESS", 10) - - else: # use environment variables - print( - "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\nWARNING : edit secrets.json.example and save as secrets.json\n!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" - ) - print("Using Xplaceholder values for direct firmware.bin modification.") - wifi_ssid = "XplaceholderWIFISSID____________" - wifi_password = "XplaceholderWIFIPASSWORD________________________________________" - remote_url = "XplaceholderREMOTEURL___________________________________________________________________________________________________________" - refresh_interval_seconds = ( - 10 # int(os.environ.get("REFRESH_INTERVAL_SECONDS")) - ) - default_brightness = ( - 30 # int(os.environ.get("DEFAULT_BRIGHTNESS")) - ) + # Load secrets configuration based on available files + config = load_secrets_config() + # Apply configuration to build flags env.Append( CCFLAGS=[ - f"-DWIFI_SSID={env.StringifyMacro(wifi_ssid)}", - f"-DWIFI_PASSWORD={env.StringifyMacro(wifi_password)}", - f"-DREMOTE_URL={env.StringifyMacro(remote_url)}", - f"-DREFRESH_INTERVAL_SECONDS={refresh_interval_seconds}", - f"-DDEFAULT_BRIGHTNESS={default_brightness}", + f"-DWIFI_SSID={env.StringifyMacro(config['wifi_ssid'])}", + f"-DWIFI_PASSWORD={env.StringifyMacro(config['wifi_password'])}", + f"-DREMOTE_URL={env.StringifyMacro(config['remote_url'])}", + f"-DREFRESH_INTERVAL_SECONDS={config['refresh_interval_seconds']}", + f"-DDEFAULT_BRIGHTNESS={config['default_brightness']}", ], ) + # Print final configuration summary + print(f"🔧 Build configuration loaded from: {config['source']}") + if config['source'] != 'placeholder': + print(f" SSID: {config['wifi_ssid']}") + print(f" URL: {config['remote_url']}") + print(f" Refresh: {config['refresh_interval_seconds']}s") + print(f" Brightness: {config['default_brightness']}") + if config['source'] == 'secrets.json.injected': + print(" â„šī¸ Using previously injected configuration") + else: + print(" Using placeholder values - firmware will need manual configuration") + main() diff --git a/platformio.ini b/platformio.ini index 8d24c27..ee8d5c3 100644 --- a/platformio.ini +++ b/platformio.ini @@ -14,8 +14,9 @@ framework = espidf monitor_speed = 115200 ; monitor_rts = 0 ; monitor_dtr = 0 -extra_scripts = +extra_scripts = pre:extra_scripts/pre.py + post:extra_scripts/post.py extra_scripts/reset.py monitor_filters = direct diff --git a/secrets.json.example b/secrets.json.example index 36b8387..97c8ed2 100644 --- a/secrets.json.example +++ b/secrets.json.example @@ -1,7 +1,7 @@ { "WIFI_SSID": "myssiD", "WIFI_PASSWORD": "PASSWORD", - "REMOTE_URL": "http://192.168.1.10:8000/admin/tronbyt_1/next", + "REMOTE_URL": "http://192.168.1.10:8000/ababababab/next", "REFRESH_INTERVAL_SECONDS": 10, "DEFAULT_BRIGHTNESS" : 30 } \ No newline at end of file