Architecture¶
The package is app; run.py and python -m app start it. The wire protocol is described in the Protocol Specification.
flowchart LR
Client[HTTP client] --> API[app.api]
API --> GW[app.gateway.RctGateway]
GW --> SER[app.scheduling serializer]
SER --> TR[app.transport endpoint]
TR -->|TCP 8899| INV[(Inverter)]
Modules¶
| Package | Role |
|---|---|
app/__main__.py | CLI: serve, validate |
app/config.py | Settings loading and validation |
app/admin | Admin sessions, password setup, encrypted SQLite persistence, PATs, GUI templates and local assets |
app/api | FastAPI app factory (app_factory.py), routers, RFC 9457 problems, middleware, body limit, server |
app/security | PAT format and verification, rate limit, client address |
app/gateway | DeviceGateway port and vendor-neutral DTOs (base.py); RctGateway (rct.py) is the only adapter that turns metric names into frames |
app/catalog | Object registry loaded from app/catalog/objects.json |
app/allowlist.py | Validates write targets and values; admin selection is persisted in data/rct.db |
app/protocol | Frames, CRC, escaping, stream parser, value codecs, net.slave_data |
app/transport | TransportEndpoint owns the TCP connection to one endpoint; demultiplexer, receiver, send gate |
app/scheduling | Access serializer (one worker and bounded FIFO queue per endpoint), work budget, retry, single-flight, periodic reads, heartbeat, shutdown |
app/cache.py | Value store with freshness handling |
app/observability | Prometheus exporter, names and statistics |
app/dispatch | Battery dispatch: state machine and recovery (controller.py), per-device capabilities and gate, SoC-target policy, encrypted crash-durable store |
app/energy | Energy Manager: business actions in front of battery dispatch, operating mode (off/manual/external), readings port |
app/export | Optional push export to InfluxDB 2 / QuestDB, QuestDB provisioning |
Request path¶
A request is authenticated and rate limited in the API layer, resolved to a device and metric through the catalog, and handed to the gateway. The gateway submits a transaction to the endpoint's serializer, which executes one transaction at a time against the inverter. Reads may be served from the cache; fresh=true forces a device transaction.
Adapter port¶
Another device vendor implements the DeviceGateway protocol from app/gateway/base.py and fills the same DTOs, without changing paths, error keys or status codes.