Documentație

Tot ce ai nevoie ca să trimiți citiri sau să conectezi un invertor. Dacă folosești un agent AI, indică-i /llms.txt.

Quick start

Create an API key in Settings → API keys. Every request authenticates with Authorization: Bearer <key> (or X-Api-Key, or PVOutput's X-Pvoutput-Apikey). Verify it, then push a reading:

curl https://tomoshi.energy/api/v1/me -H "Authorization: Bearer rt_live_…"
curl https://tomoshi.energy/api/v1/readings \
  -H "Authorization: Bearer rt_live_…" -H "Content-Type: application/json" \
  -d '{"watts": 2450, "energy_today_kwh": 7.3}'

Readings

Fields are optional except that at least one of watts, energy_today_*, energy_total_* must be present.

  • at — ISO 8601 with offset, or unix seconds. Omitted → server time. Rejected if > 1 h in the future or > 14 days old (use outputs for history).
  • watts — integer 0…1,000,000
  • energy_today_wh / energy_today_kwh — today's counter
  • energy_total_wh / energy_total_kwh — lifetime counter
  • temp_c, voltage — floats

Same timestamp twice = one reading (upsert), so retries are safe. A batch is one request against the 600/hour limit; over it you get 429 with Retry-After.

Daily totals are derived, in this order: max of today's counter · lifetime counter delta (reset-safe) · integration of the power curve (gaps > 2 h count as zero, shown as ≈ estimate).

{ "readings": [
  { "at": "2026-06-15T12:00:00+03:00", "watts": 3120, "energy_today_wh": 9400 },
  { "at": 1781784300, "watts": 3050 }
] }
→ 202 { "ok": true, "accepted": 2, "rejected": [] }

Home Assistant

A rest_command plus a time_pattern automation pushes every 5 minutes. Replace the sensor entity ids with yours.

rest_command:
  rooftop_push:
    url: "https://tomoshi.energy/api/v1/readings"
    method: POST
    headers:
      Authorization: "Bearer rt_live_…"
      Content-Type: "application/json"
    payload: >
      {"watts": {{ states('sensor.inverter_power') | int }},
       "energy_today_kwh": {{ states('sensor.inverter_energy_today') | float }}}

automation:
  - alias: Push solar to rooftop
    trigger:
      - platform: time_pattern
        minutes: "/5"
    action:
      - service: rest_command.rooftop_push

Migrating from PVOutput

Already uploading to PVOutput? Change the host only. The same parameter names work: d (yyyymmdd), t (hh:mm, your local time), v1 energy Wh, v2 power W, v5 temperature, v6 voltage, c1 = 1 or 2 when v1 is a lifetime counter. sid is accepted and ignored.

curl -X POST https://tomoshi.energy/service/r2/addstatus.jsp \
  -H "X-Pvoutput-Apikey: rt_live_…" \
  -d "d=20260615&t=12:05&v1=9400&v2=3120"
→ OK 200: Added Status
curl -X POST https://tomoshi.energy/service/r2/addbatchstatus.jsp \
  -H "X-Pvoutput-Apikey: rt_live_…" \
  -d "data=20260615,12:00,9200,3050;20260615,12:05,9400,3120"

Plain cron

Any box that can run curl can push. Read your inverter however you like, then:

*/5 * * * * W=$(cat /var/run/inverter/watts) && curl -s https://tomoshi.energy/api/v1/readings \
  -H "Authorization: Bearer rt_live_…" -H "Content-Type: application/json" \
  -d "{\"watts\": $W}" > /dev/null

Historical import

POST /api/v1/outputs stores whole days you already know the total for (from an old PVOutput export, a bill, an inverter app). Fields: date (YYYY-MM-DD, your local day), energy_wh or energy_kwh, optional peak_watts. No recency limit. Days are marked *declared* and drawn dashed. A date that already has measured readings is refused with 409 and the offending dates — nothing is written for that call. This endpoint is enabled per account.

{ "outputs": [
  { "date": "2025-08-01", "energy_kwh": 24.1, "peak_watts": 4100 },
  { "date": "2025-08-02", "energy_kwh": 22.7 }
] }

Pull integrations

Instead of pushing, connect a source in Settings → Integrations and our worker polls it every 5–10 minutes:

  • Huawei SmartLogger / SUN2000 over Modbus TCP — the device must be reachable from our worker (a VPN/WireGuard peer, or a port-forward you control). Host, port (502), unit id (0 for a SmartLogger, 1 for an inverter).
  • FusionSolar kiosk link — generate a public kiosk URL in the FusionSolar app and paste it. Huawei's cloud refreshes every 5–30 minutes, so the curve is coarser than push.

Readings from pull sources go through the same pipeline as pushed ones.

Endpoints

MethodPathAuthWhat
GET/api/v1/meAPI keyVerify a key: { ok, username, timezone, isPublic }. Call this first when debugging.
POST/api/v1/readingsAPI keyPush readings: one object, an array, or { readings: [...] }; max 1000; 202 with accepted/rejected.
POST/api/v1/outputsAPI keyImport whole declared days (date + energy). 409 if a date already has readings. Feature-flagged.
GET|POST/service/r2/addstatus.jspX-Pvoutput-ApikeyPVOutput-compatible single status (d, t, v1, v2, v5, v6, c1).
GET|POST/service/r2/addbatchstatus.jspX-Pvoutput-ApikeyPVOutput-compatible batch: data=d,t,v1,v2,v5,v6;…
GET/api/healthnoneService health: db, roof status counts, worker heartbeat.