Add support for inverter current caps in site configuration
- Introduced `InverterModbusCurrentCapsBody` model for updating max charge and discharge currents. - Updated SQL queries to utilize `COALESCE` for effective current limits. - Modified relevant tests to reflect changes in battery current handling. - Added new SQL migration for `deye_register_max_current_a` columns in the database.
This commit is contained in:
@@ -7,12 +7,24 @@ from typing import Annotated, Any
|
||||
|
||||
import asyncpg
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from app.db_json import record_to_dict
|
||||
from app.deps import get_pg_pool
|
||||
|
||||
router = APIRouter(prefix="/sites/{site_id}", tags=["sites"])
|
||||
|
||||
|
||||
class InverterModbusCurrentCapsBody(BaseModel):
|
||||
"""Tvrdý strop proudu pro zápis Deye reg 108/109 (A); NULL ve JSONu = smaž strop v DB."""
|
||||
|
||||
deye_register_max_charge_a: int | None = Field(
|
||||
default=None, ge=0, le=640, description="None při vynechání klíče = nezměnit; explicitní null = smazat strop"
|
||||
)
|
||||
deye_register_max_discharge_a: int | None = Field(
|
||||
default=None, ge=0, le=640, description="Jako u nabíjení"
|
||||
)
|
||||
|
||||
_DEYE_KEYS = frozenset(
|
||||
{
|
||||
"deye_last_system_time_sync_at",
|
||||
@@ -246,3 +258,71 @@ async def get_site_configuration(
|
||||
"active_plan_created_at": _iso_utc(run_row["created_at"]) if run_row else None,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@router.patch("/inverters/{inverter_id}/modbus-current-caps")
|
||||
async def patch_inverter_modbus_current_caps(
|
||||
site_id: int,
|
||||
inverter_id: int,
|
||||
body: InverterModbusCurrentCapsBody,
|
||||
pool: Annotated[asyncpg.Pool, Depends(get_pg_pool)],
|
||||
) -> dict[str, Any]:
|
||||
"""
|
||||
Nastavení `deye_register_max_charge_a` / `deye_register_max_discharge_a` na `ems.asset_inverter`.
|
||||
Hodnoty se uplatní v dotazu `_load_inverter_config` jako `COALESCE(strop_A, FLOOR(…z_kW))` pro reg 108/109.
|
||||
"""
|
||||
updates = body.model_dump(exclude_unset=True)
|
||||
if not updates:
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail="Send at least one of: deye_register_max_charge_a, deye_register_max_discharge_a",
|
||||
)
|
||||
async with pool.acquire() as conn:
|
||||
owner = await conn.fetchval(
|
||||
"""
|
||||
SELECT id FROM ems.asset_inverter
|
||||
WHERE id = $1 AND site_id = $2
|
||||
""",
|
||||
inverter_id,
|
||||
site_id,
|
||||
)
|
||||
if owner is None:
|
||||
raise HTTPException(status_code=404, detail="Inverter not found for this site")
|
||||
|
||||
sets: list[str] = []
|
||||
args: list[Any] = []
|
||||
n = 1
|
||||
if "deye_register_max_charge_a" in updates:
|
||||
sets.append(f"deye_register_max_charge_a = ${n}")
|
||||
args.append(updates["deye_register_max_charge_a"])
|
||||
n += 1
|
||||
if "deye_register_max_discharge_a" in updates:
|
||||
sets.append(f"deye_register_max_discharge_a = ${n}")
|
||||
args.append(updates["deye_register_max_discharge_a"])
|
||||
n += 1
|
||||
|
||||
args.extend([inverter_id, site_id])
|
||||
await conn.execute(
|
||||
f"""
|
||||
UPDATE ems.asset_inverter
|
||||
SET {", ".join(sets)}
|
||||
WHERE id = ${n} AND site_id = ${n + 1}
|
||||
""",
|
||||
*args,
|
||||
)
|
||||
row = await conn.fetchrow(
|
||||
"""
|
||||
SELECT id, code, deye_register_max_charge_a, deye_register_max_discharge_a
|
||||
FROM ems.asset_inverter
|
||||
WHERE id = $1 AND site_id = $2
|
||||
""",
|
||||
inverter_id,
|
||||
site_id,
|
||||
)
|
||||
assert row is not None
|
||||
return {
|
||||
"inverter_id": int(row["id"]),
|
||||
"code": row["code"],
|
||||
"deye_register_max_charge_a": row["deye_register_max_charge_a"],
|
||||
"deye_register_max_discharge_a": row["deye_register_max_discharge_a"],
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user