Step One added—JSONL export toggle is now in your phone-first gateway.

Download:


What changed (precisely)

  1. New flags
  • --export
    Enables JSONL export (ClickHouse‑ready JSONEachRow).
  • --export-dir <path>
    Directory to write JSONL files. Defaults to exports.
    Tip (Termux): use your SD card for easy access, e.g. /sdcard/solveforce/exports.
  1. When it writes
  • On every plugin read—whether triggered by the poller or by a manual /read request.
  • File per plugin: <export-dir>/<plugin>.jsonl.
  • Row shape (one JSON object per line): {"plugin":"battery","_ts":"2025-08-19T05:55:00.123Z","data":{...}}
  1. Safety
  • Exports are best-effort and never crash the server.
  • If the path is invalid, runtime continues; you’ll see the status in /health.

Quick start on Android

Termux

# Optional: storage + Termux:API for battery reads
termux-setup-storage
pkg install -y python termux-api

# Run (local only)
python solveforce_phone_one.py --host 127.0.0.1 --port 8080 --poll 0

# Run with exports to SD card + poll every 5s, visible on LAN
python solveforce_phone_one.py --host 0.0.0.0 --port 8080 --poll 5 --export --export-dir /sdcard/solveforce/exports

Open on the phone: http://127.0.0.1:8080/ui
From another device on Wi‑Fi: http://PHONE_IP:8080/ui


Verify it’s exporting

  1. Hit /health — you’ll see:
{
  "status":"ok",
  "export":{"enabled": true, "dir": "/sdcard/solveforce/exports"},
  ...
}
  1. Trigger some reads:
  • /read?plugin=all (exports rows for battery and net)
  • Or let the poller run (--poll 5) and check the folder:
    • /sdcard/solveforce/exports/battery.jsonl
    • /sdcard/solveforce/exports/net.jsonl
  1. Tail a file (Termux):
tail -f /sdcard/solveforce/exports/battery.jsonl

ClickHouse ingestion (when you’re ready)

Example tables:

CREATE TABLE gw_battery
(
  plugin String,
  _ts    DateTime64(3, 'UTC'),
  data   JSON
)
ENGINE = MergeTree
ORDER BY _ts;

CREATE TABLE gw_net
(
  plugin String,
  _ts    DateTime64(3, 'UTC'),
  data   JSON
)
ENGINE = MergeTree
ORDER BY _ts;

Load:

INSERT INTO gw_battery FORMAT JSONEachRow
-- paste the contents of battery.jsonl here;

INSERT INTO gw_net FORMAT JSONEachRow
-- paste the contents of net.jsonl here;

Notes & next toggles (coming steps if you want them)

  • Step 2 — plugin scaffolder & auto‑loader: plugins/ directory on the phone; /admin/refresh to discover new plugins.
  • Step 3 — richer metrics: per‑plugin read counters, last_ok timestamps, and /events (SSE) for a live dashboard.
  • Step 4 — network/grid readers: SNMP/NETCONF/gNMI/OpenADR/IEC‑61850/DNP3; still read‑only.

Say “Step two” and I’ll add the plugin scaffolder and dynamic loader immediately.