#!/usr/bin/env python3 """Server connection test script — run on the Pi 4. Secrets (API_URL/API_KEY) are loaded from config_local.py (via config).""" import hashlib import hmac import sys import time import subprocess from config import API_URL, API_KEY, DEVICE_ID, DEVICE_LOCATION, SENSORS signed_ok = False # whether the signed POST returned 200 — setup.sh gates hardening on this exit code # Test with the first sensor configured on this device — hardcoding sensor_id (e.g. 1) # would pollute another site node's leak_sensor_status when testing from that site. TEST_SENSOR_ID = SENSORS[0]["id"] TEST_SENSOR_NAME = SENSORS[0]["name"] # Extract the base path from API_URL (.../api/sensor_data.php → .../api) API_BASE = API_URL.rsplit("/", 1)[0] print("=" * 50) print(" Server connection test (Pi 4)") print("=" * 50) print(f" API_BASE = {API_BASE}") # ── Step 1: call test.php with curl ── print("\n[1/3] Checking test.php connectivity with curl...") try: result = subprocess.run( ["curl", "-s", "-o", "/dev/null", "-w", "%{http_code}", "-A", "LeakSensor/2.0", "-X", "POST", f"{API_BASE}/test.php"], capture_output=True, text=True, timeout=10 ) code = result.stdout.strip() print(f" HTTP response code: {code}") if code == "200": print(" → Success! Server connection OK") elif code == "403": print(" → 403 Forbidden: the server/host itself is blocking") print(" → Check the hosting settings or the upstream .htaccess") else: print(f" → Unexpected response: {code}") except Exception as e: print(f" → curl failed: {e}") # ── Step 2: call sensor_data.php with curl ── print("\n[2/3] Checking sensor_data.php connectivity with curl...") try: result = subprocess.run( ["curl", "-s", "-w", "\n%{http_code}", "-A", "LeakSensor/2.0", "-X", "POST", API_URL, "-H", "Content-Type: application/json", "-d", f'{{"device_id":"test","sensor_id":{TEST_SENSOR_ID},"event_type":"startup","is_leak":0,"timestamp":1234567890}}'], capture_output=True, text=True, timeout=10 ) lines = result.stdout.strip().split("\n") code = lines[-1] if lines else "?" body = "\n".join(lines[:-1]) print(f" HTTP response code: {code}") print(f" Response body: {body[:200]}") except Exception as e: print(f" → curl failed: {e}") # ── Step 3: signed call via Python requests ── print("\n[3/3] Calling sensor_data.php with Python requests...") try: import requests # raw-body X-Signature HMAC (same as the server's verify_signature_raw — build the body # string with the same fixed field order as leak_sensor.leak_event_body, sign those bytes, # then POST them verbatim). ts = int(time.time()) body = ( '{' f'"device_id":"{DEVICE_ID}",' f'"device_location":"{DEVICE_LOCATION}",' f'"sensor_id":{TEST_SENSOR_ID},' f'"sensor_name":"{TEST_SENSOR_NAME} (connection test)",' f'"event_type":"startup",' f'"is_leak":0,' f'"timestamp":{ts}' '}' ) body_bytes = body.encode("utf-8") signature = hmac.new(API_KEY.encode("utf-8"), body_bytes, hashlib.sha256).hexdigest() resp = requests.post( API_URL, data=body_bytes, timeout=10, headers={ "Content-Type": "application/json", "X-Device-Id": DEVICE_ID, "X-Signature": signature, "User-Agent": "LeakSensor/2.0", } ) print(f" HTTP response code: {resp.status_code}") print(f" Response body: {resp.text[:200]}") if resp.status_code == 200: signed_ok = True print(" → Success! All connections OK") elif resp.status_code == 403: print(" → 403: the requests library is blocked or the signature does not match") print(f" → User-Agent: {resp.request.headers.get('User-Agent', '?')}") else: print(f" → Response code: {resp.status_code}") except ImportError: print(" → requests not installed. Run install.sh or sudo apt install python3-requests") except Exception as e: print(f" → Failed: {e}") print("\n" + "=" * 50) print(" Test complete" + ("" if signed_ok else " — signed POST failed (check the log above)")) print("=" * 50) sys.exit(0 if signed_ok else 1)