- Run app on Raspberry Pi: serial to ESP32 bridge at 912000 baud, /dev/ttyS0 - Remove ESP-NOW/MicroPython-only code from src (espnow, p2p, wifi, machine/Pin) - Transport: always send 6-byte MAC + payload; optional to/destination_mac in API and WebSocket - Settings and model DB use project paths (no root); fix sys.print_exception for CPython - Preset/settings controllers use get_current_sender(); template paths for cwd=src - Pipfile: run from src, PORT from env; scripts for port 80 (setcap) and test - ESP32 bridge: receive 6-byte addr + payload, LRU peer management (20 max), handle ESP_ERR_ESPNOW_EXIST - Add esp32/main.py, esp32/benchmark_peers.py, scripts/setup-port80.sh, scripts/test-port80.sh Made-with: Cursor
75 lines
2.6 KiB
Python
75 lines
2.6 KiB
Python
import json
|
|
import os
|
|
import binascii
|
|
|
|
|
|
def _settings_path():
|
|
"""Path to settings.json in project root (writable without root)."""
|
|
try:
|
|
base = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
return os.path.join(base, "settings.json")
|
|
except Exception:
|
|
return "settings.json"
|
|
|
|
|
|
class Settings(dict):
|
|
SETTINGS_FILE = None # Set in __init__ from _settings_path()
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
if Settings.SETTINGS_FILE is None:
|
|
Settings.SETTINGS_FILE = _settings_path()
|
|
self.load() # Load settings from file during initialization
|
|
|
|
def generate_secret_key(self):
|
|
"""Generate a random secret key for session signing."""
|
|
try:
|
|
# Try to use os.urandom for secure random bytes
|
|
random_bytes = os.urandom(32)
|
|
return binascii.hexlify(random_bytes).decode('utf-8')
|
|
except (AttributeError, NotImplementedError):
|
|
# Fallback for MicroPython or systems without os.urandom
|
|
try:
|
|
import secrets
|
|
return secrets.token_hex(32)
|
|
except ImportError:
|
|
# Last resort: use a combination of time and random
|
|
import time
|
|
import random
|
|
random.seed(time.time())
|
|
return binascii.hexlify(bytes([random.randint(0, 255) for _ in range(32)])).decode('utf-8')
|
|
|
|
def set_defaults(self):
|
|
"""Set default settings if they don't exist."""
|
|
if 'session_secret_key' not in self:
|
|
self['session_secret_key'] = self.generate_secret_key()
|
|
# Save immediately when generating a new key
|
|
self.save()
|
|
|
|
def save(self):
|
|
try:
|
|
j = json.dumps(self)
|
|
with open(self.SETTINGS_FILE, 'w') as file:
|
|
file.write(j)
|
|
print("Settings saved successfully.")
|
|
except Exception as e:
|
|
print(f"Error saving settings: {e}")
|
|
|
|
def load(self):
|
|
loaded_from_file = False
|
|
try:
|
|
with open(self.SETTINGS_FILE, 'r') as file:
|
|
loaded_settings = json.load(file)
|
|
self.update(loaded_settings)
|
|
loaded_from_file = True
|
|
print("Settings loaded successfully.")
|
|
except Exception as e:
|
|
print(f"Error loading settings")
|
|
self.clear()
|
|
finally:
|
|
# Ensure defaults are set even if file exists but is missing keys
|
|
self.set_defaults()
|
|
# Only save if file didn't exist or was invalid
|
|
if not loaded_from_file:
|
|
self.save()
|