Zum Inhalt springen

← Web API - GET | Inhaltsverzeichnis | Weiter: Sensor-Kalibrierung →

9. Web API - POST-Endpunkte

Version: 1.0.0
Date: 2025-01-15

Ausführliche Dokumentation der REST API POST-Endpunkte zur Steuerung und Konfiguration des ACRouter-Systems.




Inhaltsverzeichnis




9.1 Einführung

Die REST API POST-Endpunkte des ACRouter dienen folgenden Zwecken:

  • Router-Steuerung – Betriebsmodus und Dimmer-Stufe ändern
  • Konfiguration – Systemparameter aktualisieren
  • WiFi-Verwaltung – Verbindung zu Netzwerken herstellen/trennen
  • Hardware-Konfiguration – GPIOs, Sensoren, Dimmer konfigurieren
  • Systemsteuerung – Gerät neu starten

Alle Änderungen werden sofort übernommen und im NVS (Non-Volatile Storage) gespeichert.

Implementierung: components/comm/src/WebServerManager.cpp




9.2 Anfrageformat


9.2.1 Content-Type

Alle POST-Anfragen müssen folgenden Header verwenden:

http
Content-Type: application/json


9.2.2 Anfragestruktur

http
POST /api/endpoint HTTP/1.1
Host: 192.168.4.1
Content-Type: application/json

{
  "parameter1": "value1",
  "parameter2": 123
}


9.2.3 Erfolgsantwort-Format

HTTP-Status: 200 OK

json
{
  "success": true,
  "message": "Operation completed successfully"
}


9.2.4 Fehlerantwort-Format

HTTP-Status: 400, 500, 501

json
{
  "error": "Error message description"
}



9.3 Endpunkt: POST /api/config

Systemkonfigurationsparameter aktualisieren. Es können ein oder mehrere Parameter in einer einzelnen Anfrage aktualisiert werden.


9.3.1 Anfrage

http
POST /api/config HTTP/1.1
Host: 192.168.4.1
Content-Type: application/json

{
  "voltage_coef": 1.02,
  "current_coef": 30.0,
  "current_threshold": 0.12,
  "power_threshold": 12.0,
  "control_gain": 180.0,
  "balance_threshold": 40.0
}


9.3.2 Parameter (optional)

Alle Parameter sind optional. Nur die angegebenen Felder werden aktualisiert.

Parameter Typ Bereich Beschreibung
voltage_coef float 0.1 .. 10.0 Kalibrierungskoeffizient für Spannung
current_coef float 0.1 .. 100.0 Strom-Umrechnungskoeffizient (A/V)
current_threshold float 0.01 .. 10.0 Strom-Erkennungsschwelle (A)
power_threshold float 1.0 .. 1000.0 Leistung-Erkennungsschwelle (W)
control_gain float 1.0 .. 1000.0 Regelkreis-Verstärkung
balance_threshold float 0.0 .. 100.0 Ausgleichsschwelle für AUTO-Modus (W)


9.3.3 Antwort (Erfolg)

HTTP-Status: 200 OK

json
{
  "success": true,
  "message": "Configuration updated"
}

Wenn keine Parameter geändert wurden:

json
{
  "success": true,
  "message": "No changes"
}


9.3.4 Antwort (Fehler)

HTTP-Status: 400 Bad Request

json
{
  "error": "Missing request body"
}
json
{
  "error": "Invalid JSON"
}


9.3.5 Anwendungsbeispiele

Einzelnen Parameter aktualisieren:

bash
curl -X POST http://192.168.4.1/api/config \
  -H "Content-Type: application/json" \
  -d '{"control_gain": 200.0}'

Mehrere Parameter aktualisieren:

bash
curl -X POST http://192.168.4.1/api/config \
  -H "Content-Type: application/json" \
  -d '{
    "control_gain": 180.0,
    "balance_threshold": 40.0,
    "voltage_coef": 1.02
  }'

Python:

python
import requests

config = {
    "control_gain": 200.0,
    "balance_threshold": 35.0
}

response = requests.post(
    "http://192.168.4.1/api/config",
    json=config
)

print(response.json())
# {'success': True, 'message': 'Configuration updated'}

JavaScript:

javascript
const config = {
  control_gain: 200.0,
  balance_threshold: 35.0
};

fetch('http://192.168.4.1/api/config', {
  method: 'POST',
  headers: {'Content-Type': 'application/json'},
  body: JSON.stringify(config)
})
  .then(r => r.json())
  .then(data => console.log(data));



9.4 Endpunkt: POST /api/config/reset

Alle Konfigurationsparameter auf Werkseinstellungen zurücksetzen.


9.4.1 Anfrage

http
POST /api/config/reset HTTP/1.1
Host: 192.168.4.1

Anfrage-Body: Nicht erforderlich


9.4.2 Werkseinstellungen

cpp
control_gain        = 150.0
balance_threshold   = 50.0 W
voltage_coef        = 1.0
current_coef        = 30.0 A/V
current_threshold   = 0.1 A
power_threshold     = 10.0 W


9.4.3 Antwort (Erfolg)

HTTP-Status: 200 OK

json
{
  "success": true,
  "message": "Configuration reset to defaults"
}


9.4.4 Anwendungsbeispiele

Bash:

bash
curl -X POST http://192.168.4.1/api/config/reset

Python:

python
import requests

response = requests.post("http://192.168.4.1/api/config/reset")
print(response.json())
# {'success': True, 'message': 'Configuration reset to defaults'}

⚠️ Warnung: Dieser Vorgang ist unwiderruflich. Alle benutzerdefinierten Einstellungen gehen verloren.




9.5 Endpunkt: POST /api/mode

Betriebsmodus des Routers festlegen.


9.5.1 Anfrage

http
POST /api/mode HTTP/1.1
Host: 192.168.4.1
Content-Type: application/json

{
  "mode": "auto"
}


9.5.2 Parameter

Parameter Typ Erforderlich Gültige Werte
mode string Ja "off", "auto", "eco", "offgrid", "manual", "boost"

Modusbeschreibungen:

Modus Beschreibung
"off" Router deaktiviert, Dimmer 0%
"auto" Solar-Router – Netzimport/-export minimieren
"eco" Sparmodus – Import vermeiden, Export erlauben
"offgrid" Offgrid – Solar/Batterie, minimaler Netzbezug
"manual" Manuell – feste Dimmer-Stufe
"boost" Maximale Leistung – Dimmer 100%


9.5.3 Antwort (Erfolg)

HTTP-Status: 200 OK

json
{
  "success": true,
  "message": "Mode set to AUTO"
}


9.5.4 Antwort (Fehler)

HTTP-Status: 400 Bad Request

json
{
  "error": "Missing 'mode' field"
}
json
{
  "error": "Invalid mode (use: off, auto, eco, offgrid, manual, boost)"
}


9.5.5 Anwendungsbeispiele

AUTO-Modus setzen (Solar-Router):

bash
curl -X POST http://192.168.4.1/api/mode \
  -H "Content-Type: application/json" \
  -d '{"mode": "auto"}'

ECO-Modus setzen:

bash
curl -X POST http://192.168.4.1/api/mode \
  -H "Content-Type: application/json" \
  -d '{"mode": "eco"}'

Python – zwischen Modi umschalten:

python
import requests
import time

def set_mode(mode):
    response = requests.post(
        "http://192.168.4.1/api/mode",
        json={"mode": mode}
    )
    return response.json()

# Set AUTO mode
print(set_mode("auto"))
time.sleep(60)

# Switch to MANUAL for testing
print(set_mode("manual"))
time.sleep(30)

# Return to AUTO
print(set_mode("auto"))



9.6 Endpunkt: POST /api/dimmer

Dimmer-Stufe festlegen. Schaltet den Router automatisch in den MANUAL-Modus.


9.6.1 Anfrage

http
POST /api/dimmer HTTP/1.1
Host: 192.168.4.1
Content-Type: application/json

{
  "value": 75
}


9.6.2 Parameter

Parameter Typ Erforderlich Bereich Beschreibung
value integer Ja 0 .. 100 Dimmer-Stufe in Prozent


9.6.3 Antwort (Erfolg)

HTTP-Status: 200 OK

json
{
  "success": true,
  "message": "Dimmer value set"
}

⚠️ Wichtig: Der Router schaltet beim Setzen der Dimmer-Stufe automatisch in den MANUAL-Modus.


9.6.4 Antwort (Fehler)

HTTP-Status: 400 Bad Request

json
{
  "error": "Missing 'value' field"
}
json
{
  "error": "Value must be 0-100"
}


9.6.5 Anwendungsbeispiele

Dimmer auf 50% setzen:

bash
curl -X POST http://192.168.4.1/api/dimmer \
  -H "Content-Type: application/json" \
  -d '{"value": 50}'

Python – sanfte Leistungsrampe:

python
import requests
import time

def set_dimmer(value):
    response = requests.post(
        "http://192.168.4.1/api/dimmer",
        json={"value": value}
    )
    return response.json()

# Gradually increase from 0% to 100%
for level in range(0, 101, 10):
    print(f"Setting dimmer to {level}%")
    set_dimmer(level)
    time.sleep(5)

# Gradually decrease back
for level in range(100, -1, -10):
    print(f"Setting dimmer to {level}%")
    set_dimmer(level)
    time.sleep(5)

JavaScript – Slider-Steuerung:

javascript
// HTML: 

const slider = document.getElementById('dimmerSlider');

slider.addEventListener('change', (e) => {
  const value = parseInt(e.target.value);

  fetch('http://192.168.4.1/api/dimmer', {
    method: 'POST',
    headers: {'Content-Type': 'application/json'},
    body: JSON.stringify({value: value})
  })
    .then(r => r.json())
    .then(data => console.log(`Dimmer set to ${value}%`));
});



9.7 Endpunkt: POST /api/manual

Alternativer Endpunkt zum Setzen des manuellen Modus mit festgelegter Dimmer-Stufe. Entspricht POST /api/mode (mode=manual) + POST /api/dimmer.


9.7.1 Anfrage

http
POST /api/manual HTTP/1.1
Host: 192.168.4.1
Content-Type: application/json

{
  "value": 60
}


9.7.2 Parameter

Parameter Typ Erforderlich Bereich Beschreibung
value integer Ja 0 .. 100 Dimmer-Stufe in Prozent


9.7.3 Verhalten

  1. Schaltet den Router in den MANUAL-Modus
  2. Setzt die Dimmer-Stufe auf den angegebenen Wert


9.7.4 Antwort (Erfolg)

HTTP-Status: 200 OK

json
{
  "success": true,
  "message": "Manual control set"
}


9.7.5 Anwendungsbeispiele

Bash:

bash
curl -X POST http://192.168.4.1/api/manual \
  -H "Content-Type: application/json" \
  -d '{"value": 60}'

Unterschied zwischen /api/dimmer und /api/manual:

python
import requests

# Option 1: Use /api/dimmer
# Automatically switches to MANUAL
requests.post("http://192.168.4.1/api/dimmer", json={"value": 60})

# Option 2: Use /api/manual
# Explicitly switches to MANUAL and sets level
requests.post("http://192.168.4.1/api/manual", json={"value": 60})

# Result is identical



9.8 Endpunkt: POST /api/hardware/voltage/calibrate

Automatische Kalibrierung des Spannungssensors (verfügbar ab Version 2.0).

Das System misst automatisch den Sensorausgang (VDC RMS) und berechnet den Kalibrierungsmultiplikator basierend auf der gemessenen Netzspannung.


9.8.1 Anfrage

http
POST /api/hardware/voltage/calibrate HTTP/1.1
Host: 192.168.4.1
Content-Type: application/json

{
  "measured_vac": 232.5
}


9.8.2 Parameter

Parameter Typ Erforderlich Beschreibung
measured_vac float Ja Mit Multimeter gemessene Netzspannung (VAC RMS)

Bereich: 100.0 .. 300.0 (abhängig vom Netzstandard)


9.8.3 Kalibrierungsvorgang

  1. PowerMeterADC misst den aktuellen Sensorausgang (VDC RMS)
  2. Berechnet den Multiplikator: multiplier = measured_vac / sensor_vdc
  3. Aktualisiert nominal_vdc auf den tatsächlich gemessenen Wert
  4. Konfiguration wird im NVS gespeichert
  5. ⚠️ Neustart erforderlich, um Änderungen zu übernehmen


9.8.4 Antwort (Erfolg)

HTTP-Status: 200 OK

json
{
  "success": true,
  "message": "Voltage sensor calibrated",
  "sensor_vdc": 0.72,
  "multiplier": 322.92,
  "sensor_type": "ZMPT107"
}

Antwortfelder:

Feld Typ Beschreibung
sensor_vdc float Gemessener Sensorausgang (VDC RMS)
multiplier float Berechneter Multiplikator
sensor_type string Sensortyp (ZMPT107, ZMPT101B, CUSTOM)


9.8.5 Antwort (Fehler)

HTTP-Status: 400 Bad Request

json
{
  "error": "Missing 'measured_vac' field"
}
json
{
  "error": "PowerMeterADC is not running"
}
json
{
  "error": "No voltage sensor configured"
}

HTTP-Status: 500 Internal Server Error

json
{
  "error": "Failed to save calibration to NVS"
}


9.8.6 Anwendungsbeispiele

Einfache Kalibrierung:

bash
curl -X POST http://192.168.4.1/api/hardware/voltage/calibrate \
  -H "Content-Type: application/json" \
  -d '{"measured_vac": 232.5}'

Beispielantwort:

json
{
  "success": true,
  "message": "Voltage sensor calibrated",
  "sensor_vdc": 0.72,
  "multiplier": 322.92,
  "sensor_type": "ZMPT107"
}

Python – vollständiger Kalibrierungsablauf:

python
import requests
import time

def calibrate_voltage_sensor(measured_vac, base_url="http://192.168.4.1"):
    """
    Automatic voltage sensor calibration

    Args:
        measured_vac: Grid voltage measured with multimeter (VAC)
        base_url: Router URL

    Returns:
        bool: True if calibration successful
    """

    print(f"Starting voltage sensor calibration...")
    print(f"Measured grid voltage: {measured_vac} VAC")

    # 1. Send calibration request
    response = requests.post(
        f"{base_url}/api/hardware/voltage/calibrate",
        json={"measured_vac": measured_vac},
        timeout=10
    )

    if response.status_code != 200:
        error = response.json().get('error', 'Unknown error')
        print(f"✗ Calibration failed: {error}")
        return False

    # 2. Get results
    result = response.json()

    if not result.get('success'):
        print(f"✗ Calibration failed")
        return False

    print(f"✓ Calibration successful:")
    print(f"  Sensor VDC: {result['sensor_vdc']:.3f} V")
    print(f"  Multiplier: {result['multiplier']:.2f}")
    print(f"  Sensor type: {result['sensor_type']}")

    # 3. Reboot device
    print("\nRebooting device to apply calibration...")
    requests.post(f"{base_url}/api/system/reboot")

    # 4. Wait for reboot
    time.sleep(15)

    # 5. Verify result
    print("Verifying calibration...")
    for attempt in range(10):
        try:
            status = requests.get(f"{base_url}/api/status", timeout=2).json()
            voltage = status.get('voltage', 0)

            # Check voltage is close to measured value
            if abs(voltage - measured_vac) < 5.0:
                print(f"✓ Calibration verified: {voltage:.1f} V")
                return True
            else:
                print(f"  Current reading: {voltage:.1f} V (expected {measured_vac:.1f} V)")
        except:
            pass

        time.sleep(2)

    print("⚠ Could not verify calibration (device may still be rebooting)")
    return False

# Usage
calibrate_voltage_sensor(232.5)

JavaScript – Kalibrierung über Weboberfläche:

javascript
async function calibrateVoltageSensor(measuredVAC) {
    try {
        // 1. Send calibration request
        const response = await fetch('http://192.168.4.1/api/hardware/voltage/calibrate', {
            method: 'POST',
            headers: {'Content-Type': 'application/json'},
            body: JSON.stringify({measured_vac: measuredVAC})
        });

        const result = await response.json();

        if (!result.success) {
            alert(`Calibration failed: ${result.error || 'Unknown error'}`);
            return false;
        }

        // 2. Show results
        alert(`Calibration successful!\n` +
              `Sensor VDC: ${result.sensor_vdc.toFixed(3)} V\n` +
              `Multiplier: ${result.multiplier.toFixed(2)}\n` +
              `Sensor: ${result.sensor_type}\n\n` +
              `Device will reboot now...`);

        // 3. Reboot device
        await fetch('http://192.168.4.1/api/system/reboot', {method: 'POST'});

        return true;
    } catch (error) {
        alert(`Error: ${error.message}`);
        return false;
    }
}

// Usage in HTML form
// 
// 


9.8.7 Wichtige Hinweise

⚠️ Voraussetzungen:

  • PowerMeterADC muss aktiv sein (isRunning() == true)
  • Ein Spannungssensor muss konfiguriert sein (Typ VOLTAGE_AC)
  • Nach der Kalibrierung ist ein Neustart erforderlich

Vorteile der automatischen Kalibrierung:

  • Kein manuelles Einstellen des Sensor-Potentiometers nötig
  • Funktioniert mit jeder Sensor-Ausgangsspannung (0,5 V, 0,7 V, 1,0 V usw.)
  • Aktualisiert nominal_vdc automatisch auf den tatsächlich gemessenen Wert
  • Genauere und schnellere Kalibrierung

📖 Siehe auch:




9.9 Endpunkt: POST /api/wifi/connect

Verbindung mit einem WiFi-Netzwerk herstellen. Die Zugangsdaten werden bei erfolgreicher Verbindung automatisch im NVS gespeichert.


9.9.1 Anfrage

http
POST /api/wifi/connect HTTP/1.1
Host: 192.168.4.1
Content-Type: application/json

{
  "ssid": "MyHomeNetwork",
  "password": "MySecurePassword123"
}


9.9.2 Parameter

Parameter Typ Erforderlich Beschreibung
ssid string Ja WiFi-Netzwerkname (max. 32 Zeichen)
password string Nein Netzwerkpasswort (leer bei offenen Netzwerken)


9.9.3 Verhalten

  1. Trennt die Verbindung zum aktuellen STA-Netzwerk (falls verbunden)
  2. Versucht, eine Verbindung zum neuen Netzwerk herzustellen
  3. Bei Erfolg:
  4. Zugangsdaten werden automatisch im NVS gespeichert
  5. IP-Adresse wird per DHCP bezogen
  6. NTP wird initialisiert (Zeitsynchronisation)
  7. AP-Modus bleibt aktiv (AP+STA-Modus)
  8. Beim nächsten Start verbindet sich der Router automatisch mit diesem Netzwerk


9.9.4 Antwort (Erfolg)

HTTP-Status: 200 OK

json
{
  "success": true,
  "message": "Connecting to WiFi... Credentials will be saved on success",
  "ssid": "MyHomeNetwork"
}

⚠️ Wichtig: Die Antwort wird sofort nach Einleitung der Verbindung gesendet. Die tatsächliche Verbindung dauert 5–10 Sekunden.


9.9.5 Antwort (Fehler)

HTTP-Status: 400 Bad Request

json
{
  "error": "Missing 'ssid' field"
}

HTTP-Status: 500 Internal Server Error

json
{
  "error": "Failed to initiate WiFi connection"
}


9.9.6 Verbindung überprüfen

Nach 10–15 Sekunden den Status prüfen:

bash
curl -s http://192.168.4.1/api/wifi/status | jq '.sta_connected, .sta_ip'


9.9.7 Anwendungsbeispiele

Mit gesichertem Netzwerk verbinden:

bash
curl -X POST http://192.168.4.1/api/wifi/connect \
  -H "Content-Type: application/json" \
  -d '{
    "ssid": "MyHomeNetwork",
    "password": "SecurePass2024"
  }'

Mit offenem Netzwerk verbinden:

bash
curl -X POST http://192.168.4.1/api/wifi/connect \
  -H "Content-Type: application/json" \
  -d '{"ssid": "PublicWiFi"}'

Python – Verbindung mit Überprüfung:

python
import requests
import time

def connect_wifi(ssid, password=None):
    """Connect to WiFi and wait for result"""

    # Send connection request
    payload = {"ssid": ssid}
    if password:
        payload["password"] = password

    response = requests.post(
        "http://192.168.4.1/api/wifi/connect",
        json=payload
    )

    if response.status_code != 200:
        print(f"Failed to initiate connection: {response.json()}")
        return False

    print(f"Connecting to {ssid}...")

    # Wait 10 seconds
    time.sleep(10)

    # Check result
    status = requests.get("http://192.168.4.1/api/wifi/status").json()

    if status['sta_connected'] and status['sta_ssid'] == ssid:
        print(f"✓ Connected to {ssid}")
        print(f"  IP: {status['sta_ip']}")
        print(f"  RSSI: {status['rssi']} dBm")
        return True
    else:
        print(f"✗ Failed to connect to {ssid}")
        return False

# Usage
connect_wifi("MyHomeNetwork", "MyPassword123")

⚠️ Sicherheit: Das Passwort wird im Klartext übertragen. Nur über direkte AP-Verbindung verwenden.




9.10 Endpunkt: POST /api/wifi/disconnect

Verbindung zum WiFi-Netzwerk trennen (STA-Modus). Der Access Point (AP) bleibt aktiv.


9.10.1 Anfrage

http
POST /api/wifi/disconnect HTTP/1.1
Host: 192.168.4.1

Anfrage-Body: Nicht erforderlich


9.10.2 Verhalten

  • Trennt die Verbindung zum STA-Netzwerk
  • Wechselt in den AP_ONLY-Modus
  • Gespeicherte Zugangsdaten bleiben im NVS erhalten (für automatische Verbindung beim Neustart)


9.10.3 Antwort (Erfolg)

HTTP-Status: 200 OK

json
{
  "success": true,
  "message": "Disconnected from WiFi network"
}


9.10.4 Anwendungsbeispiele

Bash:

bash
curl -X POST http://192.168.4.1/api/wifi/disconnect

Python:

python
import requests

response = requests.post("http://192.168.4.1/api/wifi/disconnect")
print(response.json())
# {'success': True, 'message': 'Disconnected from WiFi network'}

# Check status
status = requests.get("http://192.168.4.1/api/wifi/status").json()
print(f"State: {status['state']}")  # AP_ONLY
print(f"Saved credentials: {status['has_saved_credentials']}")  # True (remain in NVS)



9.11 Endpunkt: POST /api/wifi/forget

Gespeicherte WiFi-Zugangsdaten aus dem NVS löschen.


9.11.1 Anfrage

http
POST /api/wifi/forget HTTP/1.1
Host: 192.168.4.1

Anfrage-Body: Nicht erforderlich


9.11.2 Verhalten

  • Löscht WiFi-Zugangsdaten aus dem NVS
  • Aktuelle Verbindung bleibt bestehen (falls verbunden)
  • Beim nächsten Start wird sich der Router NICHT automatisch verbinden
  • Bleibt im AP_ONLY-Modus


9.11.3 Antwort (Erfolg)

HTTP-Status: 200 OK

json
{
  "success": true,
  "message": "WiFi credentials cleared from NVS"
}


9.11.4 Antwort (Fehler)

HTTP-Status: 500 Internal Server Error

json
{
  "error": "Failed to clear WiFi credentials"
}


9.11.5 Anwendungsbeispiele

Bash:

bash
curl -X POST http://192.168.4.1/api/wifi/forget

Python – vollständiger WiFi-Reset:

python
import requests

def reset_wifi():
    """Complete WiFi reset"""

    # 1. Disconnect from network
    print("Disconnecting from WiFi...")
    requests.post("http://192.168.4.1/api/wifi/disconnect")

    # 2. Clear saved credentials
    print("Clearing saved credentials...")
    response = requests.post("http://192.168.4.1/api/wifi/forget")

    if response.json()['success']:
        print("✓ WiFi reset complete")
        print("  Router will be in AP-only mode after reboot")
    else:
        print("✗ Failed to clear credentials")

reset_wifi()

Typisches Anwendungsszenario:

python
# Change WiFi network

# 1. Clear old credentials
requests.post("http://192.168.4.1/api/wifi/forget")

# 2. Connect to new network
requests.post("http://192.168.4.1/api/wifi/connect", json={
    "ssid": "NewNetwork",
    "password": "NewPassword"
})



9.12 Endpunkt: POST /api/hardware/config

Hardware-Konfiguration aktualisieren: ADC-Kanäle, Dimmer, Nulldurchgang, Relais, LEDs.

⚠️ Wichtig: Änderungen werden erst nach einem Geräteneustart übernommen.


9.12.1 Anfrage

http
POST /api/hardware/config HTTP/1.1
Host: 192.168.4.1
Content-Type: application/json

{
  "adc_channels": [
    {
      "gpio": 34,
      "type": 1,
      "multiplier": 320.0,
      "offset": 0.0,
      "enabled": true
    },
    ...
  ],
  "dimmer_ch1": {
    "gpio": 19,
    "enabled": true
  },
  "zerocross_gpio": 18,
  "zerocross_enabled": true,
  ...
}


9.12.2 Parameter

ADC-Kanäle (Array mit 4 Elementen):

Parameter Typ Erforderlich Beschreibung Empfohlener Bereich
gpio integer Ja GPIO-Pin 32–39 (nur ADC1!)
type integer Ja Sensortyp 0=NONE, 1=VOLTAGE_AC, 2=CURRENT_LOAD, 3=CURRENT_GRID, 4=CURRENT_SOLAR
multiplier float Nein Kalibrierungskoeffizient Auto: 328,57 (Spannung 230 V), 30,0 (Strom). Siehe Sensor-Kalibrierung. Bereich: 0,1–1000,0
offset float Nein Kalibrierungs-Offset Standard: 0,0. Bereich: -100,0 bis +100,0
enabled boolean Ja Aktiviert true/false

Hinweise:

  • GPIO: Nur ADC1 (32–39). ADC2 wird im kontinuierlichen Lesemodus nicht unterstützt!
  • Multiplikator: Wird automatisch je nach Sensortyp gewählt, falls nicht angegeben:
  • VOLTAGE_AC → 230,0 (ZMPT107)
  • CURRENT_* → 30,0 (SCT013-030)
  • Offset: Dient zur Kompensation eines systematischen Sensorfehlers

Dimmer:

Parameter Typ Beschreibung
dimmer_ch1.gpio integer GPIO-Pin für Dimmer 1
dimmer_ch1.enabled boolean Dimmer 1 aktiviert
dimmer_ch2.gpio integer GPIO-Pin für Dimmer 2
dimmer_ch2.enabled boolean Dimmer 2 aktiviert

Nulldurchgangsdetektor:

Parameter Typ Beschreibung
zerocross_gpio integer GPIO-Pin
zerocross_enabled boolean Aktiviert

Relais:

Parameter Typ Beschreibung
relay_ch1.gpio integer GPIO-Pin für Relais 1
relay_ch1.active_high boolean Logik: true = HIGH-aktiv
relay_ch1.enabled boolean Relais 1 aktiviert
relay_ch2.* ... Identisch für Relais 2

LED-Anzeigen:

Parameter Typ Beschreibung
led_status_gpio integer GPIO für Status-LED
led_load_gpio integer GPIO für Last-LED


9.12.3 Validierung

Die Konfiguration wird vor dem Speichern automatisch validiert:

  • Prüfung auf GPIO-Konflikte (ein GPIO kann nicht doppelt verwendet werden)
  • Prüfung auf gültige GPIO-Pins (0–39)
  • Prüfung der Sensortypen


9.12.4 Antwort (Erfolg)

HTTP-Status: 200 OK

json
{
  "success": true,
  "message": "Configuration saved to NVS (reboot required)"
}

⚠️ Wichtig: Ein Neustart ist erforderlich, um die Änderungen zu übernehmen!


9.12.5 Antwort (Validierungsfehler)

HTTP-Status: 400 Bad Request

json
{
  "success": false,
  "error": "GPIO 19 conflict: used by Dimmer 1 and ADC channel 0"
}


9.12.6 Anwendungsbeispiele

ADC-Kanäle aktualisieren:

bash
curl -X POST http://192.168.4.1/api/hardware/config \
  -H "Content-Type: application/json" \
  -d '{
    "adc_channels": [
      {
        "gpio": 34,
        "type": 1,
        "multiplier": 320.0,
        "offset": 0.0,
        "enabled": true
      },
      {
        "gpio": 35,
        "type": 2,
        "multiplier": 30.0,
        "offset": 0.0,
        "enabled": true
      }
    ]
  }'

Python – Dimmer-GPIO ändern:

python
import requests

config = {
    "dimmer_ch1": {
        "gpio": 21,  # Change from 19 to 21
        "enabled": True
    }
}

response = requests.post(
    "http://192.168.4.1/api/hardware/config",
    json=config
)

if response.json()['success']:
    print("Configuration saved. Rebooting device...")
    # Reboot device
    requests.post("http://192.168.4.1/api/system/reboot")



9.13 Endpunkt: POST /api/hardware/validate

Hardware-Konfiguration validieren, ohne zu speichern. Nützlich zur Prüfung vor der Anwendung.


9.13.1 Anfrage

http
POST /api/hardware/validate HTTP/1.1
Host: 192.168.4.1
Content-Type: application/json

{
  "dimmer_ch1": {
    "gpio": 34,
    "enabled": true
  }
}


9.13.2 Antwort (Konfiguration gültig)

HTTP-Status: 200 OK

json
{
  "valid": true
}


9.13.3 Antwort (Konfiguration ungültig)

HTTP-Status: 200 OK

json
{
  "valid": false,
  "error": "GPIO 34 conflict: already used by ADC channel 0"
}


9.13.4 Anwendungsbeispiele

Python – vor der Anwendung validieren:

python
import requests

new_config = {
    "dimmer_ch1": {"gpio": 21, "enabled": True},
    "zerocross_gpio": 18
}

# 1. Validate first
validate_response = requests.post(
    "http://192.168.4.1/api/hardware/validate",
    json=new_config
)

if validate_response.json()['valid']:
    print("✓ Configuration valid, applying...")

    # 2. Apply configuration
    apply_response = requests.post(
        "http://192.168.4.1/api/hardware/config",
        json=new_config
    )

    if apply_response.json()['success']:
        print("✓ Configuration saved, rebooting...")
        requests.post("http://192.168.4.1/api/system/reboot")
else:
    print(f"✗ Invalid configuration: {validate_response.json()['error']}")



9.14 Endpunkt: POST /api/system/reboot

Gerät neu starten.


9.14.1 Anfrage

http
POST /api/system/reboot HTTP/1.1
Host: 192.168.4.1

Anfrage-Body: Nicht erforderlich


9.14.2 Verhalten

  1. Sendet die Antwort an den Client
  2. Wartet 500 ms (zur Übertragung der Antwort)
  3. Wartet weitere 3 Sekunden
  4. Führt Neustart über ESP.restart() aus

⚠️ Wichtig: Kritische Aufgaben sollten vor dem Neustart beendet werden (für zukünftige Versionen geplant).


9.14.3 Antwort

HTTP-Status: 200 OK

json
{
  "success": true,
  "message": "Rebooting in 3 seconds..."
}


9.14.4 Anwendungsbeispiele

Bash:

bash
curl -X POST http://192.168.4.1/api/system/reboot

Python – Neustart und warten:

python
import requests
import time

def reboot_and_wait(url="http://192.168.4.1"):
    """Reboot device and wait for it to come back online"""

    print("Rebooting device...")
    try:
        requests.post(f"{url}/api/system/reboot", timeout=5)
    except requests.exceptions.RequestException:
        pass  # Connection may be dropped

    print("Waiting for device to reboot...")
    time.sleep(10)  # Wait 10 seconds

    # Wait until device comes back online
    for attempt in range(30):
        try:
            response = requests.get(f"{url}/api/info", timeout=2)
            if response.status_code == 200:
                info = response.json()
                print(f"✓ Device online (uptime: {info['uptime_sec']}s)")
                return True
        except requests.exceptions.RequestException:
            pass

        print(f"  Attempt {attempt + 1}/30...")
        time.sleep(2)

    print("✗ Device did not come back online")
    return False

# Usage
reboot_and_wait()



9.15 Fehlerbehandlung


9.15.1 HTTP-Fehlercodes

Code Status Beschreibung
400 Bad Request Ungültiges Anfrageformat, fehlende Felder, ungültige Werte
500 Internal Server Error Interner Serverfehler (NVS-Fehler usw.)
501 Not Implemented Funktion nicht implementiert


9.15.2 Fehlerbeispiele

Fehlender Anfrage-Body:

json
{
  "error": "Missing request body"
}

Ungültiges JSON:

json
{
  "error": "Invalid JSON"
}

Fehlendes Pflichtfeld:

json
{
  "error": "Missing 'mode' field"
}

Ungültiger Wert:

json
{
  "error": "Value must be 0-100"
}

Validierungsfehler:

json
{
  "error": "GPIO 19 conflict: used by Dimmer 1 and ADC channel 0"
}


9.15.3 Fehlerbehandlung im Code

Python:

python
import requests

def safe_post(url, json_data):
    """Safe POST request with error handling"""
    try:
        response = requests.post(url, json=json_data, timeout=10)

        if response.status_code == 200:
            return response.json()
        else:
            error_msg = response.json().get('error', 'Unknown error')
            print(f"Error {response.status_code}: {error_msg}")
            return None

    except requests.exceptions.Timeout:
        print("Request timeout")
        return None

    except requests.exceptions.ConnectionError:
        print("Connection error")
        return None

    except Exception as e:
        print(f"Unexpected error: {e}")
        return None

# Usage
result = safe_post(
    "http://192.168.4.1/api/mode",
    {"mode": "auto"}
)

if result and result.get('success'):
    print("Success!")
else:
    print("Failed to set mode")



9.16 Anwendungsbeispiele


9.16.1 Vollständige Router-Konfiguration (Python)

python
import requests
import time

class ACRouterConfig:
    def __init__(self, base_url="http://192.168.4.1"):
        self.base_url = base_url

    def set_mode(self, mode):
        """Set operating mode"""
        return requests.post(
            f"{self.base_url}/api/mode",
            json={"mode": mode}
        ).json()

    def set_dimmer(self, value):
        """Set dimmer level"""
        return requests.post(
            f"{self.base_url}/api/dimmer",
            json={"value": value}
        ).json()

    def update_config(self, **params):
        """Update configuration parameters"""
        return requests.post(
            f"{self.base_url}/api/config",
            json=params
        ).json()

    def connect_wifi(self, ssid, password=None):
        """Connect to WiFi"""
        payload = {"ssid": ssid}
        if password:
            payload["password"] = password

        response = requests.post(
            f"{self.base_url}/api/wifi/connect",
            json=payload
        ).json()

        if response['success']:
            time.sleep(10)  # Wait for connection
            status = requests.get(f"{self.base_url}/api/wifi/status").json()
            return status['sta_connected']

        return False

    def setup_solar_router(self):
        """Complete Solar Router setup"""

        # 1. Configure control parameters
        print("Configuring control parameters...")
        self.update_config(
            control_gain=180.0,
            balance_threshold=40.0
        )

        # 2. Set AUTO mode
        print("Setting AUTO mode...")
        self.set_mode("auto")

        # 3. Check status
        status = requests.get(f"{self.base_url}/api/status").json()
        print(f"Mode: {status['mode']}")
        print(f"Dimmer: {status['dimmer']}%")
        print(f"Power: {status['power_grid']}W")

# Usage
router = ACRouterConfig()
router.setup_solar_router()


9.16.2 Überwachung und automatische Korrektur (Python)

python
import requests
import time

def monitor_and_adjust():
    """Monitor power and automatically adjust mode"""

    base_url = "http://192.168.4.1"

    while True:
        # Get current status
        status = requests.get(f"{base_url}/api/status").json()

        power = status['power_grid']
        mode = status['mode']

        print(f"[{time.strftime('%H:%M:%S')}] Power: {power:+7.1f}W | Mode: {mode}")

        # If high import from grid in AUTO mode
        if mode == 'auto' and power > 500:
            print("  High import detected, increasing gain...")
            requests.post(f"{base_url}/api/config", json={"control_gain": 250.0})

        # If high export to grid
        elif mode == 'auto' and power < -500:
            print("  High export detected, switching to BOOST mode...")
            requests.post(f"{base_url}/api/mode", json={"mode": "boost"})

        time.sleep(10)

monitor_and_adjust()


9.16.3 Web-Steuerpanel (HTML + JavaScript)

html



    ACRouter Control Panel
    


    

ACRouter Control Panel

Mode Control

Dimmer Control

0%

Quick Actions




  • 07_COMMANDS.md – Befehlsreferenz (RU)
  • 07_COMMANDS_EN.md – Befehlsreferenz (EN)
  • 08_WEB_API_GET.md – Web API GET-Endpunkte (RU)
  • 08_WEB_API_GET_EN.md – Web API GET-Endpunkte (EN)
  • 09_WEB_API_POST.md – Web API POST-Endpunkte (RU)
  • 10_WEB_INTERFACE.md – Navigation der Weboberfläche (nächster Abschnitt)

Firmware Version: 1.0.0
Last Updated: 2025-01-15

← Web API - GET | Inhaltsverzeichnis | Weiter: Sensor-Kalibrierung →