JK BMS
SK EN DE

API a dokumentácia

Obe gateway vystavujú v lokálnej sieti jednoduché HTTP JSON API. Čítanie je bez prihlásenia, každá zmena (POST) vyžaduje PIN v hlavičke X-PIN (z výroby 666666). Vyberte produkt, prezrite si endpointy a začnite s ukážkovým kódom.

Všetky odpovede sú JSON. Príklad: curl http://jkbms.local/api/realtime

⚠️ Viac batérií na jednej RS485 zbernici? Adresu 0 nikdy nepoužívajte. Batéria nastavená na adresu 0 sa sama nepretržite správa ako „master” a prehľadáva celú zbernicu — to blokuje čítanie z nej aj zo susedných batérií, aj keď je zapojenie, ukončovacie odpory aj uzemnenie úplne správne. Nastavte adresy všetkých batérií cez DIP prepínače (alebo appku výrobcu) na hodnoty 1–15.
MetódaCestaPopis
GET /api/info Stav brány (IP, uptime, posledné čítanie)
GET /api/realtime[?addr=N] Napätia článkov, V/I/P batérie, SOC, teploty, alarmy
GET /api/settings[?addr=N] Aktuálne hodnoty ochranných parametrov
POST /api/settings[?addr=N] Telo: {"nazovPola": hodnota, ...} — zapíše zmenené polia
GET /api/deviceinfo[?addr=N] Vendor ID, sériové číslo, verzia HW/SW, počet zapnutí (cachované na adresu)
GET /api/scan Preskenuje adresy 0–15, vráti ktoré odpovedajú
GET /api/can Dekódovaný súhrn z CAN vysielania meniča (SOC, V/I/T, limity, príznaky, výrobca)
GET /api/can/raw[?n=20] Posledné surové CAN rámce (id, dlc, data[], vek v ms)
GET / POST /api/can/config Nastavenie CAN — prenosová rýchlosť, autoscan, profil (ukladá sa do NVS)
GET / POST /api/server-config Nastavenia web servera — časovanie RS485 čítania (oneskorenie zápis→čítanie, interval pollovania, timeout odpovede) a voliteľné čítania (ukladá sa do NVS, aplikuje sa okamžite)
GET /api/logs História logov od posledného bootovania (výsledky pollovania, udalosti CAN/WiFi/licencie) — len v pamäti, resetuje sa pri reštarte
GET /api/history[?minutes=N] Minútové priemery sledovanej batérie (napätie, prúd, SOC) za posledných N minút, max. 24 h — údaje pre graf v dashboarde
GET /api/debug/read?reg=0xHEX&count=N[&addr=N] Surové čítanie Modbus registrov
POST /api/debug/verifywrite?reg=0xHEX[&addr=N] Prečíta register, zapíše rovnakú hodnotu späť, znova prečíta — overí zápisovú cestu bez zmeny
POST /api/wifi/reset Znovu otvorí WiFi konfiguračný portál, aby sa dala zmeniť sieť
POST /api/pin Zmení PIN pre zmeny, telo {"newPin":"1234"} (4–8 číslic). Súčasný PIN ide v hlavičke X-PIN. Vypnúť sa nedá.
GET /api/firmware/check Brána sa spýta jkbms.sk, či existuje novšia verzia firmvéru
POST /api/firmware/update Nainštaluje najnovšie podpísané vydanie z jkbms.sk (PIN a overená licencia); brána sa po 1–2 minútach reštartuje do novej verzie

Ukážkový kód

Každá ukážka urobí jeden GET /api/realtime a vypíše napätie, prúd a SOC. Adresu gateway zadáte ako parameter, predvolená je jej mDNS adresa.

curl + jq (shell)

Stiahnuť jkbms_realtime.sh

#!/usr/bin/env bash
# JK BMS Web Gateway — read live pack data with curl + jq.
#
# One HTTP GET, no authentication; the gateway answers JSON, or
# {"error": "..."} when the BMS did not respond.
#
# Usage: ./jkbms_realtime.sh [http://jkbms.local]
#   (use the gateway's IP, e.g. http://192.168.1.50, if mDNS is not reachable)
set -euo pipefail

BASE_URL="${1:-http://jkbms.local}"

curl -sS --max-time 5 "$BASE_URL/api/realtime" | jq -r '
  if .error then error("gateway error: " + .error) else
    "Pack voltage: \(.totalVoltage) V\n" +
    "Current:      \(.current) A\n" +
    "SOC:          \(.soc) %"
  end'

Python 3 (bez závislostí)

Stiahnuť jkbms_realtime.py

#!/usr/bin/env python3
"""JK BMS Web Gateway — read live pack data. Standard library only, no pip installs.

Usage: python3 jkbms_realtime.py [http://jkbms.local]
  (use the gateway's IP, e.g. http://192.168.1.50, if mDNS is not reachable)
"""
import json
import sys
import urllib.request


def fetch_realtime(base_url: str) -> dict:
    """GET /api/realtime and return the decoded JSON.

    Raises on network errors and when the gateway reports {"error": ...}
    (BMS not answering, wrong address, ...).
    """
    with urllib.request.urlopen(f"{base_url}/api/realtime", timeout=5) as response:
        data = json.load(response)
    if "error" in data:
        raise RuntimeError(f"gateway error: {data['error']}")
    return data


def main() -> int:
    """Prints voltage, current, SOC and the cell spread; returns the process exit code."""
    base_url = sys.argv[1] if len(sys.argv) > 1 else "http://jkbms.local"
    try:
        realtime = fetch_realtime(base_url)
    except Exception as ex:  # network, JSON or gateway error: one line, non-zero exit
        print(f"request failed: {ex}", file=sys.stderr)
        return 1

    print(f"Pack voltage: {realtime['totalVoltage']:.2f} V")
    print(f"Current:      {realtime['current']:.2f} A  (positive = charging)")
    print(f"SOC:          {realtime['soc']} %")
    print(f"Cells:        {realtime['minCellVoltage']:.3f} .. {realtime['maxCellVoltage']:.3f} V")
    return 0


if __name__ == "__main__":
    sys.exit(main())

JavaScript (Node.js alebo prehliadač)

Stiahnuť jkbms_realtime.js

// JK BMS Web Gateway — read live pack data with fetch().
//
// Runs as-is on Node.js 18+:  node jkbms_realtime.js [http://jkbms.local]
// The gateway sends "Access-Control-Allow-Origin: *", so fetchRealtime()
// works unchanged inside a web page too (drop the process.* lines there).

const baseUrl = process.argv[2] ?? "http://jkbms.local";

// One GET, no authentication; rejects on network errors and on {"error": ...}
async function fetchRealtime(url) {
  const response = await fetch(`${url}/api/realtime`, { signal: AbortSignal.timeout(5000) });
  const data = await response.json();
  if (data.error) {
    throw new Error(`gateway error: ${data.error}`);
  }
  return data;
}

fetchRealtime(baseUrl)
  .then((realtime) => {
    console.log(`Pack voltage: ${realtime.totalVoltage.toFixed(2)} V`);
    console.log(`Current:      ${realtime.current.toFixed(2)} A`);
    console.log(`SOC:          ${realtime.soc} %`);
  })
  .catch((error) => {
    console.error(`request failed: ${error.message}`);
    process.exitCode = 1;
  });

C/C++ konzolový klient (libcurl + nlohmann/json)

Stiahnuť jkbms_client_example.cpp

// JK BMS Web Gateway — minimal REST API console client.
//
// Fetches GET /api/realtime from the gateway and prints pack voltage,
// current, and state of charge. Demonstrates the smallest useful client:
// one HTTP GET, one JSON parse. See the full endpoint list and JSON field
// reference on the site's API documentation page (/dokumentacia).
//
// Build (Linux/macOS, needs libcurl + nlohmann/json):
//   g++ -std=c++17 jkbms_client_example.cpp -lcurl -o jkbms_client_example
// Run:
//   ./jkbms_client_example http://jkbms.local

#include <curl/curl.h>
#include <nlohmann/json.hpp>
#include <cstdio>
#include <cstdlib>
#include <string>

using nlohmann::json;

// libcurl calls this once per received chunk of the HTTP response body;
// appending to a std::string is the standard way to buffer a small response.
static size_t appendToBuffer(char *data, size_t size, size_t count, void *userData) {
    auto *buffer = static_cast<std::string *>(userData);
    buffer->append(data, size * count);
    return size * count;
}

// Performs one blocking HTTP GET and returns the response body.
// Throws std::runtime_error if the transfer itself fails (not on HTTP
// error status — the caller is expected to check the JSON's own "error" field).
static std::string httpGet(const std::string &url) {
    CURL *curl = curl_easy_init();
    if (!curl) {
        throw std::runtime_error("curl_easy_init failed");
    }

    std::string body;
    curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, appendToBuffer);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, &body);
    curl_easy_setopt(curl, CURLOPT_TIMEOUT, 5L);

    CURLcode result = curl_easy_perform(curl);
    curl_easy_cleanup(curl);

    if (result != CURLE_OK) {
        throw std::runtime_error(curl_easy_strerror(result));
    }
    return body;
}

int main(int argc, char *argv[]) {
    // Default to the gateway's mDNS name; override with a bare IP if mDNS
    // isn't reachable on your network (e.g. "http://192.168.1.50").
    std::string baseUrl = (argc > 1) ? argv[1] : "http://jkbms.local";

    std::string body;
    try {
        body = httpGet(baseUrl + "/api/realtime");
    } catch (const std::exception &ex) {
        std::fprintf(stderr, "request failed: %s\n", ex.what());
        return 1;
    }

    json realtime = json::parse(body, /*cb*/ nullptr, /*allow_exceptions*/ false);
    if (realtime.is_discarded()) {
        std::fprintf(stderr, "invalid JSON response: %s\n", body.c_str());
        return 1;
    }
    if (realtime.contains("error")) {
        std::fprintf(stderr, "gateway error: %s\n", realtime["error"].get<std::string>().c_str());
        return 1;
    }

    std::printf("Pack voltage: %.2f V\n", realtime.value("totalVoltage", 0.0));
    std::printf("Current:      %.2f A\n", realtime.value("current", 0.0));
    std::printf("SOC:          %d %%\n", realtime.value("soc", 0));
    return 0;
}

Všetky odpovede sú JSON. Príklad: curl http://deye.local/api/realtime

MetódaCestaPopis
GET /api/info Stav brány: verzia firmvéru, IP/SSID, uptime, výsledok posledného čítania, stav NTP.
GET /api/realtime Aktuálne dáta meniča z cache (beh, relé, výkony, batéria, energie, poruchy).
GET /api/settings Aktuálne nastavenia (čítanie registrov 60–347).
POST /api/settings Telo: {"pole": hodnota, ...} — zapíše jedno alebo viac nastavení, s overením spätným čítaním.
GET /api/deviceinfo Typ zariadenia, sériové číslo, verzie firmvéru, menovitý výkon.
GET /api/history?minutes=N 24-hodinová história minútových priemerov (PV, záťaž, sieť, batéria, SOC) pre grafy dashboardu; minutes = 1–1440.
GET / POST /api/server-config Nastavenia webového servera brány: interval čítania z meniča, voliteľná predpoveď počasia a spotové ceny elektriny pre dashboard.
GET /api/logs História logov od posledného bootovania (výsledky pollovania meniča) — len v pamäti, resetuje sa pri reštarte.
GET /api/debug/read?reg=N&count=N Surové čítanie Modbus registrov ­— ľubovoľný register, počet 1–100.
POST /api/debug/verifywrite?reg=N Prečíta register, zapíše rovnakú hodnotu späť, znova prečíta — neškodné overenie zápisovej cesty.
POST /api/time/sync Zapíše čas gateway (NTP) do hodín meniča.
POST /api/wifi/reset Znovu otvorí WiFi konfiguračný portál „DEYE-Setup“, aby sa dala zmeniť sieť.
POST /api/pin Zmení PIN pre zmeny, telo {"newPin":"1234"} (4–8 číslic). Súčasný PIN ide v hlavičke X-PIN. Vypnúť sa nedá.
GET /api/firmware/check Gateway sa spýta jkbms.sk, či existuje novšia verzia DEYE firmvéru.
POST /api/firmware/update Nainštaluje najnovšie podpísané vydanie z jkbms.sk (PIN a overená licencia); brána sa po 1–2 minútach reštartuje do novej verzie.

Ukážkový kód

Každá ukážka urobí jeden GET /api/realtime a vypíše napätie, prúd a SOC. Adresu gateway zadáte ako parameter, predvolená je jej mDNS adresa.

curl + jq (shell)

Stiahnuť deye_realtime.sh

#!/usr/bin/env bash
# DEYE Web Gateway — read live inverter data with curl + jq.
#
# One HTTP GET, no authentication; the gateway answers JSON, or
# {"error": "..."} when the inverter did not respond over Modbus.
#
# Usage: ./deye_realtime.sh [http://deye.local]
#   (use the gateway's IP if mDNS is not reachable)
set -euo pipefail

BASE_URL="${1:-http://deye.local}"

curl -sS --max-time 5 "$BASE_URL/api/realtime" | jq -r '
  if .error then error("gateway error: " + .error) else
    "State:    \(.runStateText)\n" +
    "Battery:  \(.batteryVoltage) V  \(.batteryCurrent) A  SOC \(.batterySoc) %\n" +
    "PV:       \(.pvPowerTotal) W\n" +
    "Load:     \(.loadPowerTotal) W\n" +
    "Grid:     \(.gridPowerTotal) W  (positive = importing)"
  end'

Python 3 (bez závislostí)

Stiahnuť deye_realtime.py

#!/usr/bin/env python3
"""DEYE Web Gateway — read live inverter data. Standard library only, no pip installs.

Usage: python3 deye_realtime.py [http://deye.local]
  (use the gateway's IP if mDNS is not reachable)
"""
import json
import sys
import urllib.request


def fetch_realtime(base_url: str) -> dict:
    """GET /api/realtime and return the decoded JSON.

    Raises on network errors and when the gateway reports {"error": ...}
    (inverter not answering over Modbus).
    """
    with urllib.request.urlopen(f"{base_url}/api/realtime", timeout=5) as response:
        data = json.load(response)
    if "error" in data:
        raise RuntimeError(f"gateway error: {data['error']}")
    return data


def main() -> int:
    """Prints the inverter state and the battery / PV / load / grid power; returns the exit code."""
    base_url = sys.argv[1] if len(sys.argv) > 1 else "http://deye.local"
    try:
        realtime = fetch_realtime(base_url)
    except Exception as ex:  # network, JSON or gateway error: one line, non-zero exit
        print(f"request failed: {ex}", file=sys.stderr)
        return 1

    print(f"State:    {realtime['runStateText']}")
    print(f"Battery:  {realtime['batteryVoltage']:.1f} V  {realtime['batteryCurrent']:.1f} A  SOC {realtime['batterySoc']} %")
    print(f"PV:       {realtime['pvPowerTotal']} W")
    print(f"Load:     {realtime['loadPowerTotal']} W")
    print(f"Grid:     {realtime['gridPowerTotal']} W  (positive = importing)")
    return 0


if __name__ == "__main__":
    sys.exit(main())