Реализация управления солнечными панелями через мобильное приложение
Солнечная система — это не просто панели. Это связка: панели → инвертор → аккумуляторный блок (опционально) → точка подключения к сети. Мобильное приложение управляет не панелями напрямую, а инвертором и системой управления накопителем. Каждый производитель инверторов — Huawei FusionSolar, SolarEdge, Fronius, SMA, GoodWe — имеет свой API или протокол. Унифицированного стандарта нет.
Протоколы инверторов
| Производитель | Локальный протокол | Облачный API |
|---|---|---|
| Huawei FusionSolar | Modbus TCP + SUN2000 SDK | FusionSolar OpenAPI |
| SolarEdge | Modbus TCP | SolarEdge Monitoring API |
| Fronius | REST (Fronius Solar API v1) | Fronius Solar.web API |
| SMA | SMA Data Manager Modbus | Sunny Portal REST |
| GoodWe | Modbus TCP | GoodWe SEMS API |
| Enphase | N/A | Enlighten API v4 |
Для большинства небольших систем используется облачный API производителя — проще, не требует открытия портов в локальной сети. Для промышленных инсталляций и офлайн-работы — локальный Modbus TCP.
Huawei FusionSolar OpenAPI
Один из популярных инверторов в Европе и СНГ. API требует авторизации через HTTPS Basic + systemCode:
// iOS
struct FusionSolarClient {
let baseURL = URL(string: "https://intl.fusionsolar.huawei.com/thirdData")!
var token: String?
mutating func login(userName: String, systemCode: String) async throws {
let body = ["userName": userName, "systemCode": systemCode]
let response: LoginResponse = try await post("/login", body: body)
token = response.data.token
}
func getStationList() async throws -> [Station] {
let response: StationListResponse = try await post(
"/getStationList",
body: ["pageNo": 1]
)
return response.data.list
}
func getRealTimeData(stationCode: String) async throws -> RealTimeData {
return try await post("/getStationRealKpi", body: ["stationCodes": stationCode])
}
}
Основные показатели из getStationRealKpi: radiation_intensity (инсоляция), theory_power (теоретическая мощность), inverter_power (реальная генерация), power_profit (выработка кВт·ч за сутки), use_power (потребление).
Fronius Solar API: локальный REST
Fronius инверторы предоставляют REST API прямо с инвертора без облака:
GET http://192.168.1.20/solar_api/v1/GetPowerFlowRealtimeData.fcgi
Ответ:
{
"Body": {
"Data": {
"Site": {
"Mode": "produce-load-grid",
"P_Grid": -1250.5,
"P_Load": -2800.0,
"P_PV": 4050.5,
"P_Akku": null,
"E_Day": 18.4,
"E_Year": 4230.0
}
}
}
}
P_Grid — отрицательное значение означает подачу в сеть. P_Load — потребление дома. P_PV — текущая генерация. Разница видна сразу: 4050 Вт генерируется, 2800 Вт потребляется, 1250 Вт отдаётся в сеть.
Fronius API опрашивается напрямую из приложения только в рамках локальной сети. Для удалённого доступа — через обратный прокси с авторизацией или через Fronius Solar.web API.
Дашборд: визуализация потоков энергии
Ключевой экран — диаграмма потоков энергии (Energy Flow Diagram): панели → дом → сеть → аккумулятор. Анимированные стрелки показывают направление потока. На Flutter:
class EnergyFlowPainter extends CustomPainter {
final double pvPower; // генерация
final double gridPower; // <0 в сеть, >0 из сети
final double loadPower; // потребление
final double batteryPower; // <0 заряд, >0 разряд
@override
void paint(Canvas canvas, Size size) {
_drawNode(canvas, pvIcon, pvPosition, '$pvPower Вт');
_drawNode(canvas, homeIcon, homePosition, '$loadPower Вт');
_drawNode(canvas, gridIcon, gridPosition, '${gridPower.abs()} Вт');
if (pvPower > 0) {
_drawAnimatedArrow(canvas, pvPosition, homePosition,
color: Colors.green, active: true);
}
if (gridPower < 0) {
_drawAnimatedArrow(canvas, homePosition, gridPosition,
color: Colors.orange, active: true);
}
}
}
Управление: режимы работы инвертора
SolarEdge и Huawei позволяют переключать режимы через API: Self-Consumption (максимум на свои нужды), Time-of-Use (заряжать аккумулятор по ночному тарифу), Export Limitation (ограничение подачи в сеть до заданного значения).
Команда через SolarEdge API:
suspend fun setStorageCommand(siteId: String, command: StorageCommand): Result<Unit> {
return withContext(Dispatchers.IO) {
runCatching {
val response = api.setStorageCommand(
siteId = siteId,
body = StorageCommandBody(
mode = command.mode.apiValue,
chargeLimit = command.chargeLimit,
dischargeLimit = command.dischargeLimit,
)
)
if (!response.isSuccessful) {
throw ApiException(response.code(), response.message())
}
}
}
}
Изменение настроек инвертора — операция с последствиями. UX должен требовать явного подтверждения и показывать текущий режим отдельно от команды, ожидающей применения.
Разработка мобильного приложения для мониторинга одной солнечной системы через облачный API: 2-3 недели. Поддержка нескольких инверторов, локальный Modbus TCP, управление режимами и автоматизации — 5-8 недель. Стоимость рассчитывается после уточнения оборудования.







