DynVoltDYNVOLT · developers

BESS

Battery-storage telemetry and the dispatch-proposal approval workflow.

The BESS surface has two halves: read everything about a battery unit (bess:read), and drive the dispatch-proposal workflow (bess:write, a control scope). Both require the bess module.

Raw hardware control is not in the API — by design. Power on/off, breaker, grid-mode, fault-reset, and protection/setup writes are owner-platform-only. bess:write covers dispatch proposals only: ask the AI for a schedule, then approve/reject/edit it.

Reads

Everything about a unit is readable:

  • Inventory & live statebess, bess/fleet/summary, bess/{id}, bess/{id}/current, bess/{id}/dashboard.
  • Historybess/{id}/history, bess/{id}/pcs/history, bess/{id}/dispatch-log, bess/{id}/control-log (all start/end + limit).
  • Internalsclusters, clusters/{idx} (cell arrays), pcs/current, pcs/faults, envelope (charge/discharge + SOC), protection, setup.
  • Alarmsalarms/states (?only_set&severity), alarms/history (?code&limit), alarms/counts.
  • Schedulesbess/{id}/schedules (?target_date), bess/schedules/{schedule_id} (one schedule with its hours) and bess/{id}/deviations (schedule-vs-actual).

See the BESS reference for every endpoint and its parameters.

The dispatch-approval workflow

Dispatch is a propose → review → approve loop. Nothing is dispatched to the battery until you approve a schedule.

  1. Optimize. POST …/bess/{bess_id}/optimize (or POST …/bess/optimize-all) runs the AI dispatch optimizer and produces a proposed schedule — a set of hourly charge/discharge targets. This does not command the battery.
  2. Review. Read the proposal with GET …/bess/schedules/{schedule_id} (or …/bess/{bess_id}/schedules?target_date=…). Inspect each hour and the projected deviations.
  3. (Optional) edit an hour. PUT …/bess/schedules/{schedule_id}/hours/{hour} revises a single hour of a proposed schedule before approval.
  4. Approve or reject. POST …/bess/schedules/{schedule_id}/approve accepts the proposal; …/reject discards it. POST …/bess/schedules/approve-all?target_date=… approves every pending schedule for a day.
# 1. Ask the AI to optimize one unit
curl -X POST "https://api.owner.dynvolt.com/v1/sites/pv-ljubas/bess/1/optimize" \
  -H "Authorization: Bearer $DYNVOLT_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)"

# 2. Review the proposed schedule (id from the response above)
curl "https://api.owner.dynvolt.com/v1/sites/pv-ljubas/bess/schedules/77" \
  -H "Authorization: Bearer $DYNVOLT_API_KEY"

# 3. Approve it
curl -X POST "https://api.owner.dynvolt.com/v1/sites/pv-ljubas/bess/schedules/77/approve" \
  -H "Authorization: Bearer $DYNVOLT_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)"
import uuid

def hdr():
    return {"Idempotency-Key": str(uuid.uuid4())}

# 1. Optimize → proposed schedule
proposal = client.post("/v1/sites/pv-ljubas/bess/1/optimize", headers=hdr()).json()
schedule_id = proposal["schedule_id"]

# 2. Review
schedule = client.get(f"/v1/sites/pv-ljubas/bess/schedules/{schedule_id}").json()

# 3. Approve (or .../reject)
client.post(
    f"/v1/sites/pv-ljubas/bess/schedules/{schedule_id}/approve",
    headers=hdr(),
)

All bess:write calls accept an Idempotency-Key and return 409 conflict_safe_mode while the site is in commissioning safe mode.