# rooftop API > Community solar production sharing. Base URL: https://tomoshi.energy ## Endpoints - GET /api/v1/me — auth: API key. Verify a key: { ok, username, timezone, isPublic }. Call this first when debugging. - POST /api/v1/readings — auth: API key. Push readings: one object, an array, or { readings: [...] }; max 1000; 202 with accepted/rejected. - POST /api/v1/outputs — auth: API key. Import whole declared days (date + energy). 409 if a date already has readings. Feature-flagged. - GET|POST /service/r2/addstatus.jsp — auth: X-Pvoutput-Apikey. PVOutput-compatible single status (d, t, v1, v2, v5, v6, c1). - GET|POST /service/r2/addbatchstatus.jsp — auth: X-Pvoutput-Apikey. PVOutput-compatible batch: data=d,t,v1,v2,v5,v6;… - GET /api/health — auth: none. Service health: db, roof status counts, worker heartbeat. ## Quick start Create an API key in Settings → API keys. Every request authenticates with `Authorization: Bearer ` (or `X-Api-Key`, or PVOutput's `X-Pvoutput-Apikey`). Verify it, then push a reading: ```bash curl https://tomoshi.energy/api/v1/me -H "Authorization: Bearer rt_live_…" ``` ```bash 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). ```json { "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. ```yaml 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. ```bash 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 ``` ```bash 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: ```bash */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. ```json { "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.