← Web API - GET | Содержание | Далее: Калибровка датчиков →
9. Web API — POST-эндпоинты
Version: 1.0.0
Date: 2025-01-15
Детальная документация по REST API POST-эндпоинтам для управления и настройки системы ACRouter.
Содержание
- 9.1 Введение
- 9.2 Формат запроса
- 9.3 Эндпоинт: POST /api/config
- 9.4 Эндпоинт: POST /api/config/reset
- 9.5 Эндпоинт: POST /api/mode
- 9.6 Эндпоинт: POST /api/dimmer
- 9.7 Эндпоинт: POST /api/manual
- 9.8 Эндпоинт: POST /api/hardware/voltage/calibrate
- 9.9 Эндпоинт: POST /api/wifi/connect
- 9.10 Эндпоинт: POST /api/wifi/disconnect
- 9.11 Эндпоинт: POST /api/wifi/forget
- 9.12 Эндпоинт: POST /api/hardware/config
- 9.13 Эндпоинт: POST /api/hardware/validate
- 9.14 Эндпоинт: POST /api/system/reboot
- 9.15 Обработка ошибок
- 9.16 Примеры использования
9.1 Введение
POST-эндпоинты REST API ACRouter предназначены для:
- Управления роутером — изменение режима работы, уровня диммера
- Конфигурации — обновление системных параметров
- Управления WiFi — подключение/отключение от сетей
- Конфигурации железа — настройка GPIO, датчиков, диммеров
- Управления системой — перезагрузка устройства
Все изменения применяются немедленно и сохраняются в NVS (энергонезависимая память).
Реализация: components/comm/src/WebServerManager.cpp
9.2 Формат запроса
9.2.1 Content-Type
Все POST-запросы должны использовать:
Content-Type: application/json
9.2.2 Структура запроса
POST /api/endpoint HTTP/1.1
Host: 192.168.4.1
Content-Type: application/json
{
"parameter1": "value1",
"parameter2": 123
}
9.2.3 Формат успешного ответа
HTTP-статус: 200 OK
{
"success": true,
"message": "Operation completed successfully"
}
9.2.4 Формат ответа с ошибкой
HTTP-статус: 400, 500, 501
{
"error": "Error message description"
}
9.3 Эндпоинт: POST /api/config
Обновить параметры конфигурации системы. Можно обновить один или несколько параметров в одном запросе.
9.3.1 Запрос
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 Параметры (все необязательные)
Все параметры необязательны. Обновляются только указанные поля.
| Параметр | Тип | Диапазон | Описание |
|---|---|---|---|
voltage_coef |
float | 0.1 .. 10.0 |
Калибровочный коэффициент напряжения |
current_coef |
float | 0.1 .. 100.0 |
Коэффициент преобразования тока (А/В) |
current_threshold |
float | 0.01 .. 10.0 |
Порог обнаружения тока (А) |
power_threshold |
float | 1.0 .. 1000.0 |
Порог обнаружения мощности (Вт) |
control_gain |
float | 1.0 .. 1000.0 |
Коэффициент усиления контура управления |
balance_threshold |
float | 0.0 .. 100.0 |
Порог баланса для режима AUTO (Вт) |
9.3.3 Ответ (успех)
HTTP-статус: 200 OK
{
"success": true,
"message": "Configuration updated"
}
Если параметры не изменялись:
{
"success": true,
"message": "No changes"
}
9.3.4 Ответ (ошибка)
HTTP-статус: 400 Bad Request
{
"error": "Missing request body"
}
{
"error": "Invalid JSON"
}
9.3.5 Примеры использования
Обновить один параметр:
curl -X POST http://192.168.4.1/api/config \
-H "Content-Type: application/json" \
-d '{"control_gain": 200.0}'
Обновить несколько параметров:
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:
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:
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 Эндпоинт: POST /api/config/reset
Сбросить все параметры конфигурации до заводских значений.
9.4.1 Запрос
POST /api/config/reset HTTP/1.1
Host: 192.168.4.1
Тело запроса: не требуется
9.4.2 Заводские значения по умолчанию
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 Ответ (успех)
HTTP-статус: 200 OK
{
"success": true,
"message": "Configuration reset to defaults"
}
9.4.4 Примеры использования
Bash:
curl -X POST http://192.168.4.1/api/config/reset
Python:
import requests
response = requests.post("http://192.168.4.1/api/config/reset")
print(response.json())
# {'success': True, 'message': 'Configuration reset to defaults'}
⚠️ Предупреждение: операция необратима. Все пользовательские настройки будут потеряны.
9.5 Эндпоинт: POST /api/mode
Установить режим работы роутера.
9.5.1 Запрос
POST /api/mode HTTP/1.1
Host: 192.168.4.1
Content-Type: application/json
{
"mode": "auto"
}
9.5.2 Параметры
| Параметр | Тип | Обязательный | Допустимые значения |
|---|---|---|---|
mode |
string | Да | "off", "auto", "eco", "offgrid", "manual", "boost" |
Описание режимов:
| Режим | Описание |
|---|---|
"off" |
Роутер отключён, диммер 0% |
"auto" |
Солнечный роутер — минимизация импорта/экспорта |
"eco" |
Экономный — защита от импорта, экспорт разрешён |
"offgrid" |
Автономный — солнечные/аккумулятор, минимальное использование сети |
"manual" |
Ручной — фиксированный уровень диммера |
"boost" |
Максимальная мощность — диммер 100% |
9.5.3 Ответ (успех)
HTTP-статус: 200 OK
{
"success": true,
"message": "Mode set to AUTO"
}
9.5.4 Ответ (ошибка)
HTTP-статус: 400 Bad Request
{
"error": "Missing 'mode' field"
}
{
"error": "Invalid mode (use: off, auto, eco, offgrid, manual, boost)"
}
9.5.5 Примеры использования
Установить режим AUTO (солнечный роутер):
curl -X POST http://192.168.4.1/api/mode \
-H "Content-Type: application/json" \
-d '{"mode": "auto"}'
Установить режим ECO:
curl -X POST http://192.168.4.1/api/mode \
-H "Content-Type: application/json" \
-d '{"mode": "eco"}'
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 Эндпоинт: POST /api/dimmer
Установить уровень диммера. Автоматически переключает роутер в режим MANUAL.
9.6.1 Запрос
POST /api/dimmer HTTP/1.1
Host: 192.168.4.1
Content-Type: application/json
{
"value": 75
}
9.6.2 Параметры
| Параметр | Тип | Обязательный | Диапазон | Описание |
|---|---|---|---|---|
value |
integer | Да | 0 .. 100 |
Уровень диммера в процентах |
9.6.3 Ответ (успех)
HTTP-статус: 200 OK
{
"success": true,
"message": "Dimmer value set"
}
⚠️ Важно: при установке уровня диммера роутер автоматически переключается в режим MANUAL.
9.6.4 Ответ (ошибка)
HTTP-статус: 400 Bad Request
{
"error": "Missing 'value' field"
}
{
"error": "Value must be 0-100"
}
9.6.5 Примеры использования
Установить диммер на 50%:
curl -X POST http://192.168.4.1/api/dimmer \
-H "Content-Type: application/json" \
-d '{"value": 50}'
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 — управление ползунком:
// 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 Эндпоинт: POST /api/manual
Альтернативный эндпоинт для установки ручного режима с заданным уровнем диммера. Эквивалентен POST /api/mode (mode=manual) + POST /api/dimmer.
9.7.1 Запрос
POST /api/manual HTTP/1.1
Host: 192.168.4.1
Content-Type: application/json
{
"value": 60
}
9.7.2 Параметры
| Параметр | Тип | Обязательный | Диапазон | Описание |
|---|---|---|---|---|
value |
integer | Да | 0 .. 100 |
Уровень диммера в процентах |
9.7.3 Поведение
- Переключает роутер в режим MANUAL
- Устанавливает уровень диммера на указанное значение
9.7.4 Ответ (успех)
HTTP-статус: 200 OK
{
"success": true,
"message": "Manual control set"
}
9.7.5 Примеры использования
Bash:
curl -X POST http://192.168.4.1/api/manual \
-H "Content-Type: application/json" \
-d '{"value": 60}'
Различие между /api/dimmer и /api/manual:
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 Эндпоинт: POST /api/hardware/voltage/calibrate
⭐ Автоматическая калибровка датчика напряжения (доступно начиная с версии 2.0).
Система автоматически измеряет выходное напряжение датчика (VDC RMS) и рассчитывает калибровочный коэффициент на основе измеренного напряжения сети.
9.8.1 Запрос
POST /api/hardware/voltage/calibrate HTTP/1.1
Host: 192.168.4.1
Content-Type: application/json
{
"measured_vac": 232.5
}
9.8.2 Параметры
| Параметр | Тип | Обязательный | Описание |
|---|---|---|---|
measured_vac |
float | Да | Напряжение сети, измеренное мультиметром (VAC RMS) |
Диапазон: 100.0 .. 300.0 (в зависимости от стандарта электросети)
9.8.3 Процесс калибровки
- PowerMeterADC измеряет текущий выход датчика (VDC RMS)
- Рассчитывает коэффициент:
multiplier = measured_vac / sensor_vdc - Обновляет
nominal_vdcдо фактически измеренного значения - Конфигурация сохраняется в NVS
- ⚠️ Для применения изменений требуется перезагрузка
9.8.4 Ответ (успех)
HTTP-статус: 200 OK
{
"success": true,
"message": "Voltage sensor calibrated",
"sensor_vdc": 0.72,
"multiplier": 322.92,
"sensor_type": "ZMPT107"
}
Поля ответа:
| Поле | Тип | Описание |
|---|---|---|
sensor_vdc |
float | Измеренный выход датчика (VDC RMS) |
multiplier |
float | Рассчитанный коэффициент |
sensor_type |
string | Тип датчика (ZMPT107, ZMPT101B, CUSTOM) |
9.8.5 Ответ (ошибка)
HTTP-статус: 400 Bad Request
{
"error": "Missing 'measured_vac' field"
}
{
"error": "PowerMeterADC is not running"
}
{
"error": "No voltage sensor configured"
}
HTTP-статус: 500 Internal Server Error
{
"error": "Failed to save calibration to NVS"
}
9.8.6 Примеры использования
Базовая калибровка:
curl -X POST http://192.168.4.1/api/hardware/voltage/calibrate \
-H "Content-Type: application/json" \
-d '{"measured_vac": 232.5}'
Python — полная процедура калибровки:
{
"success": true,
"message": "Voltage sensor calibrated",
"sensor_vdc": 0.72,
"multiplier": 322.92,
"sensor_type": "ZMPT107"
}
9.8.7 Важные замечания
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)
После калибровки требуется перезагрузка
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
//
//
Автоматически обновляет nominal_vdc до фактически измеренного значения
Более точная и быстрая калибровка
- 9.9 Эндпоинт: POST /api/wifi/connect
- Подключиться к WiFi-сети. При успешном подключении учётные данные автоматически сохраняются в NVS.
- 9.9.1 Запрос
http
- 📋 Copy
- POST /api/wifi/connect HTTP/1.1 Host: 192.168.4.1 Content-Type: application/json { "ssid": "MyHomeNetwork", "password": "MySecurePassword123" }
- 9.9.2 Параметры
- Параметр
Тип
- Обязательный Описание
ssidstring
9.9 Эндпоинт: POST /api/wifi/connect
Имя WiFi-сети (максимум 32 символа)
password
POST /api/wifi/connect HTTP/1.1
Host: 192.168.4.1
Content-Type: application/json
{
"ssid": "MyHomeNetwork",
"password": "MySecurePassword123"
}
9.9.3 Поведение
| Параметр | Тип | Обязательный | Описание |
|---|---|---|---|
| Получает IP-адрес через DHCP | string | Да | При следующей загрузке роутер автоматически подключится к этой сети |
| 9.9.4 Ответ (успех) | string | json | 📋 Copy |
{ "success": true, "message": "Connecting to WiFi... Credentials will be saved on success", "ssid": "MyHomeNetwork" }
- ⚠️ Важно: ответ отправляется сразу после инициирования подключения. Фактическое подключение занимает 5–10 секунд.
- 9.9.5 Ответ (ошибка)
- HTTP-статус:
400 Bad Request - json
- 📋 Copy
- { "error": "Missing 'ssid' field" }
- HTTP-статус:
500 Internal Server Error - json
📋 Copy
HTTP-статус: 200 OK
{
"success": true,
"message": "Connecting to WiFi... Credentials will be saved on success",
"ssid": "MyHomeNetwork"
}
📋 Copy
curl -s http://192.168.4.1/api/wifi/status | jq '.sta_connected, .sta_ip'
HTTP-статус: 400 Bad Request
{
"error": "Missing 'ssid' field"
}
HTTP-статус: 500 Internal Server Error
{
"error": "Failed to initiate WiFi connection"
}
curl -X POST http://192.168.4.1/api/wifi/connect \ -H "Content-Type: application/json" \ -d '{"ssid": "PublicWiFi"}'
Python — подключение с проверкой:
curl -s http://192.168.4.1/api/wifi/status | jq '.sta_connected, .sta_ip'
⚠️ Безопасность: пароль передаётся в открытом виде. Используйте только через прямое AP-подключение.
9.10 Эндпоинт: POST /api/wifi/disconnect
curl -X POST http://192.168.4.1/api/wifi/connect \
-H "Content-Type: application/json" \
-d '{
"ssid": "MyHomeNetwork",
"password": "SecurePass2024"
}'
📋 Copy
curl -X POST http://192.168.4.1/api/wifi/connect \
-H "Content-Type: application/json" \
-d '{"ssid": "PublicWiFi"}'
Отключается от STA-сети
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")
HTTP-статус: 200 OK
9.10 Эндпоинт: POST /api/wifi/disconnect
📋 Copy
{ "success": true, "message": "Disconnected from WiFi network" }
POST /api/wifi/disconnect HTTP/1.1
Host: 192.168.4.1
Тело запроса: не требуется
curl -X POST http://192.168.4.1/api/wifi/disconnect
- Python:
- python
- 📋 Copy
import requests response = requests.post("http://192.168.4.1/api/wifi/disconnect") print(response.json()) status = requests.get("http://192.168.4.1/api/wifi/status").json() print(f"Состояние: {status['state']}") # AP_ONLY print(f"Сохранены учётные данные: {status['has_saved_credentials']}") # True
HTTP-статус: 200 OK
{
"success": true,
"message": "Disconnected from WiFi network"
}
📋 Copy
Bash:
curl -X POST http://192.168.4.1/api/wifi/disconnect
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 Эндпоинт: POST /api/wifi/forget
json
📋 Copy
POST /api/wifi/forget HTTP/1.1
Host: 192.168.4.1
Тело запроса: не требуется
📋 Copy
- { "error": "Failed to clear WiFi credentials" }
- 9.11.5 Примеры использования
- Bash:
- bash
📋 Copy
HTTP-статус: 200 OK
{
"success": true,
"message": "WiFi credentials cleared from NVS"
}
import requests def reset_wifi(): """Полный сброс WiFi""" print("Отключение от WiFi...") requests.post("http://192.168.4.1/api/wifi/disconnect") print("Удаление сохранённых учётных данных...") response = requests.post("http://192.168.4.1/api/wifi/forget") if response.json()['success']: print("✓ WiFi сброшен") print(" После перезагрузки роутер будет только в режиме AP") else: print("✗ Не удалось удалить учётные данные") reset_wifi()
HTTP-статус: 500 Internal Server Error
{
"error": "Failed to clear WiFi credentials"
}
9.12 Эндпоинт: POST /api/hardware/config
Bash:
curl -X POST http://192.168.4.1/api/wifi/forget
📋 Copy
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()
Параметр
# 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 Эндпоинт: POST /api/hardware/config
gpio
integer
Да
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,
...
}
integer
Да
| Параметр | Тип | Обязательный | Описание | Нет |
|---|---|---|---|---|
| Калибровочный коэффициент | integer | Да | float | Нет |
| Смещение калибровки | integer | Да | boolean | Да |
multiplier |
float | json | GPIO: только ADC1 (32–39). ADC2 не поддерживается в режиме непрерывного считывания! | Multiplier: если не указан, выбирается автоматически по типу датчика: |
VOLTAGE_AC → 230.0 (ZMPT107) |
float | json | Диммеры: | Параметр |
| Тип | Описание | Да | integer | GPIO-пин диммера 1 |
dimmer_ch1.enabled
- boolean
- Диммер 1 включён
dimmer_ch2.gpio- integer
- GPIO-пин диммера 2
dimmer_ch2.enabled
| Параметр | Тип | Описание |
|---|---|---|
| Параметр | integer | Описание |
zerocross_gpio |
Описание | GPIO-пин |
zerocross_enabled |
integer | Включён |
| Реле: | Описание | Тип |
Описание
| Параметр | Тип | Описание |
|---|---|---|
relay_ch1.active_high |
integer | float |
relay_ch1.enabled |
Описание | integer |
relay_ch2.*
| Параметр | Тип | Описание |
|---|---|---|
| Параметр | integer | Описание |
led_status_gpio |
Описание | GPIO статусного LED |
led_load_gpio |
Описание | GPIO LED нагрузки |
| 9.12.3 Валидация | Перед сохранением конфигурация автоматически проверяется: | Проверка конфликтов GPIO (один GPIO не может использоваться дважды) |
Проверка корректности GPIO-пинов (0–39)
| Параметр | Тип | Описание |
|---|---|---|
| json | integer | { "success": true, "message": "Configuration saved to NVS (reboot required)" } |
| ⚠️ Важно: для применения изменений требуется перезагрузка! | integer | HTTP-статус: 400 Bad Request |
json
📋 Copy
- { "success": false, "error": "GPIO 19 conflict: used by Dimmer 1 and ADC channel 0" }
- 9.12.6 Примеры использования
- Обновить ADC-каналы:
bash
HTTP-статус: 200 OK
{
"success": true,
"message": "Configuration saved to NVS (reboot required)"
}
📋 Copy
import requests config = { "dimmer_ch1": { "gpio": 21, # Изменить с 19 на 21 "enabled": True } } response = requests.post( "http://192.168.4.1/api/hardware/config", json=config ) if response.json()['success']: print("Конфигурация сохранена. Перезагрузка...") requests.post("http://192.168.4.1/api/system/reboot")
HTTP-статус: 400 Bad Request
{
"success": false,
"error": "GPIO 19 conflict: used by Dimmer 1 and ADC channel 0"
}
📋 Copy
POST /api/hardware/validate HTTP/1.1 Host: 192.168.4.1 Content-Type: application/json { "dimmer_ch1": { "gpio": 34, "enabled": true } }
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
}
]
}'
📋 Copy
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 Эндпоинт: POST /api/hardware/validate
📋 Copy
{ "valid": false, "error": "GPIO 34 conflict: already used by ADC channel 0" }
POST /api/hardware/validate HTTP/1.1
Host: 192.168.4.1
Content-Type: application/json
{
"dimmer_ch1": {
"gpio": 34,
"enabled": true
}
}
📋 Copy
HTTP-статус: 200 OK
{
"valid": true
}
http
HTTP-статус: 200 OK
{
"valid": false,
"error": "GPIO 34 conflict: already used by ADC channel 0"
}
Отправляет ответ клиенту
Ждёт 500 мс (для передачи ответа)
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 Эндпоинт: POST /api/system/reboot
HTTP-статус: 200 OK
json
POST /api/system/reboot HTTP/1.1
Host: 192.168.4.1
Тело запроса: не требуется
bash
- 📋 Copy
- curl -X POST http://192.168.4.1/api/system/reboot
- Python — перезагрузить и дождаться:
- python
📋 Copy
import requests import time def reboot_and_wait(url="http://192.168.4.1"): """Перезагрузить устройство и дождаться его появления в сети""" print("Перезагрузка устройства...") try: requests.post(f"{url}/api/system/reboot", timeout=5) except requests.exceptions.RequestException: pass # Соединение может прерваться print("Ожидание перезагрузки...") time.sleep(10) 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"✓ Устройство онлайн (время работы: {info['uptime_sec']} с)") return True except requests.exceptions.RequestException: pass print(f" Попытка {attempt + 1}/30...") time.sleep(2) print("✗ Устройство не вышло в сеть") return False # Использование reboot_and_wait()
HTTP-статус: 200 OK
{
"success": true,
"message": "Rebooting in 3 seconds..."
}
Описание
Bash:
curl -X POST http://192.168.4.1/api/system/reboot
Internal Server Error
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 Обработка ошибок
9.15.2 Примеры ошибок
| Отсутствует тело запроса: | json | Описание |
|---|---|---|
| { "error": "Missing request body" } | Некорректный JSON: | json |
| 📋 Copy | { "error": "Invalid JSON" } | Отсутствует обязательное поле: |
| json | 📋 Copy | { "error": "Missing 'mode' field" } |
Недопустимое значение:
json
{
"error": "Missing request body"
}
json
{
"error": "Invalid JSON"
}
Python:
{
"error": "Missing 'mode' field"
}
9.16 Примеры использования
{
"error": "Value must be 0-100"
}
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): """Установить режим работы""" return requests.post( f"{self.base_url}/api/mode", json={"mode": mode} ).json() def set_dimmer(self, value): """Установить уровень диммера""" return requests.post( f"{self.base_url}/api/dimmer", json={"value": value} ).json() def update_config(self, **params): """Обновить параметры конфигурации""" return requests.post( f"{self.base_url}/api/config", json=params ).json() def connect_wifi(self, ssid, password=None): """Подключиться к 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) status = requests.get(f"{self.base_url}/api/wifi/status").json() return status['sta_connected'] return False def setup_solar_router(self): """Полная настройка солнечного роутера""" # 1. Настроить параметры управления print("Настройка параметров управления...") self.update_config( control_gain=180.0, balance_threshold=40.0 ) # 2. Установить режим AUTO print("Установка режима AUTO...") self.set_mode("auto") # 3. Проверить статус status = requests.get(f"{self.base_url}/api/status").json() print(f"Режим: {status['mode']}") print(f"Диммер: {status['dimmer']}%") print(f"Мощность: {status['power_grid']} Вт") # Использование router = ACRouterConfig() router.setup_solar_router()
{
"error": "GPIO 19 conflict: used by Dimmer 1 and ADC channel 0"
}
import requests import time def monitor_and_adjust(): """Мониторинг мощности с автоматической корректировкой режима""" base_url = "http://192.168.4.1" while True: status = requests.get(f"{base_url}/api/status").json() power = status['power_grid'] mode = status['mode'] print(f"[{time.strftime('%H:%M:%S')}] Мощность: {power:+7.1f} Вт | Режим: {mode}") # Если большой импорт из сети в AUTO-режиме if mode == 'auto' and power > 500: print(" Обнаружен высокий импорт, увеличиваем усиление...") requests.post(f"{base_url}/api/config", json={"control_gain": 250.0}) # Если высокий экспорт в сеть elif mode == 'auto' and power < -500: print(" Обнаружен высокий экспорт, переключаем в режим BOOST...") requests.post(f"{base_url}/api/mode", json={"mode": "boost"}) time.sleep(10) monitor_and_adjust()
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 Примеры использования
07_COMMANDS_RU.md — справочник команд
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()
← Web API - GET
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()
Далее: Калибровка датчиков →
ACRouter Control Panel
ACRouter Control Panel
Mode Control
Dimmer Control
0%
Quick Actions
Related Documentation
- 07_COMMANDS.md - Commands Reference (RU)
ssid- Commands Reference (EN)- 08_WEB_API_GET.md - Web API GET endpoints (RU)
- 08_WEB_API_GET_EN.md - Web API GET endpoints (EN)
- 09_WEB_API_POST.md - Web API POST endpoints (RU)
- 10_WEB_INTERFACE.md - Web interface navigation (next section)
Firmware Version: 1.0.0
Last Updated: 2025-01-15