Metadata-Version: 2.4
Name: gridos
Version: 0.3.0
Summary: Lightweight FastAPI service for DER device registration, telemetry ingestion, and basic control workflows.
Author: GridOS Contributors
License-Expression: MIT
Project-URL: Homepage, https://github.com/iceccarelli/GridOS
Project-URL: Documentation, https://github.com/iceccarelli/GridOS/tree/main/docs
Project-URL: Repository, https://github.com/iceccarelli/GridOS
Project-URL: Issues, https://github.com/iceccarelli/GridOS/issues
Keywords: energy,smart-grid,der,telemetry,fastapi
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Science/Research
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Framework :: FastAPI
Classifier: Topic :: Scientific/Engineering
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: fastapi<1.0.0,>=0.110.0
Requires-Dist: uvicorn[standard]<1.0.0,>=0.27.0
Requires-Dist: pydantic<3.0.0,>=2.6.0
Requires-Dist: pydantic-settings<3.0.0,>=2.1.0
Requires-Dist: python-dotenv<2.0.0,>=1.0.0
Requires-Dist: numpy<3.0.0,>=1.26.0
Provides-Extra: storage
Requires-Dist: influxdb-client<2.0.0,>=1.40.0; extra == "storage"
Requires-Dist: asyncpg<1.0.0,>=0.29.0; extra == "storage"
Provides-Extra: adapters
Requires-Dist: pymodbus<4.0.0,>=3.6.0; extra == "adapters"
Requires-Dist: paho-mqtt<3.0.0,>=2.0.0; extra == "adapters"
Requires-Dist: asyncua<2.0.0,>=1.1.0; extra == "adapters"
Provides-Extra: economics
Requires-Dist: pulp<3.0.0,>=2.8.0; extra == "economics"
Provides-Extra: ml
Requires-Dist: pandas<3.0.0,>=2.2.0; extra == "ml"
Requires-Dist: scikit-learn<2.0.0,>=1.4.0; extra == "ml"
Requires-Dist: matplotlib<4.0.0,>=3.8.0; extra == "ml"
Requires-Dist: joblib<2.0.0,>=1.3.0; extra == "ml"
Requires-Dist: pulp<3.0.0,>=2.8.0; extra == "ml"
Requires-Dist: pandapower>=2.14.0; extra == "ml"
Provides-Extra: dev
Requires-Dist: httpx<1.0.0,>=0.27.0; extra == "dev"
Requires-Dist: pytest<9.0.0,>=8.0.0; extra == "dev"
Requires-Dist: pytest-asyncio<1.0.0,>=0.23.0; extra == "dev"
Requires-Dist: pytest-cov<6.0.0,>=4.1.0; extra == "dev"
Requires-Dist: ruff<1.0.0,>=0.2.0; extra == "dev"
Requires-Dist: mypy<2.0.0,>=1.8.0; extra == "dev"
Requires-Dist: build<2.0.0,>=1.2.0; extra == "dev"
Requires-Dist: twine<7.0.0,>=6.1.0; extra == "dev"
Requires-Dist: pre-commit<4.0.0,>=3.6.0; extra == "dev"
Dynamic: license-file

# GridOS (under construction)

[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE)
[![Python 3.11+](https://img.shields.io/badge/python-3.11%2B-blue.svg)](https://www.python.org/downloads/)
[![CI](https://github.com/iceccarelli/GridOS/actions/workflows/ci.yml/badge.svg)](https://github.com/iceccarelli/GridOS/actions/workflows/ci.yml)
[![FastAPI](https://img.shields.io/badge/FastAPI-0.110+-teal.svg)](https://fastapi.tiangolo.com/)

**GridOS** is a lightweight FastAPI service for **DER device registration, telemetry ingestion, telemetry lookup, and basic control workflows**.

The repository still contains broader experimental areas such as digital twin simulation, forecasting, and optimisation. Those modules remain available for contributors. The supported first-run experience is intentionally smaller so that a new user can clone the repository, start the API, send telemetry, and confirm a working result without being forced into a large deployment.

## What The Current Release Supports

| Area | Supported launch scope |
|---|---|
| API service | FastAPI app with `/`, `/health`, `/docs`, device routes, telemetry routes, and control routes |
| Telemetry | Single and batch ingestion, latest-value lookup, and bounded history queries |
| Control | Command acceptance with honest adapter status reporting |
| Live device connectivity | Modbus TCP and MQTT adapters with built-in device/broker simulators; connect, poll live telemetry, and dispatch control over REST |
| Register maps | Named Modbus register maps including SunSpec-style inverter and battery presets, plus inline custom maps |
| Browser demo | A no-terminal `/demo` page that drives the full device lifecycle for prospects |
| Economic dispatch & ROI | Value-stacked battery dispatch (arbitrage + demand-charge + degradation) and a baseline-vs-optimised ROI backtest, via `/api/v1/economics/*` |
| Authentication | Optional API-key gate on mutating endpoints (`GRIDOS_REQUIRE_AUTH`), off by default |
| Default storage | In-memory mode for local development and demos |
| Persistent storage | SQLite file backend with no external service or extra dependencies |
| Optional persistence | InfluxDB and TimescaleDB when configured explicitly |
| Packaging | Editable install, reduced dependency groups, Docker image, and CI workflows |

## What Is Still Experimental

| Area | Current position |
|---|---|
| Protocol adapters | Present in the repository but not part of the launch-critical path |
| Digital twin simulation | Experimental helper modules, not required for first use |
| Forecasting | Optional module, not mounted in the default app |
| Optimisation | Optional module, not mounted in the default app |
| Large multi-service deployment | Deferred until the core local path stays stable |

## Why The Scope Is Smaller

This smaller scope is intentional. It is better for GridOS to be **simple, reliable, and truthful** than broad and internally inconsistent. The current work focuses on a dependable end-to-end path that works from a fresh clone. Experimental modules are still available for contributors, but the public launch story now matches what the repository can support immediately.

## Architecture At A Glance

```mermaid
flowchart LR
    A[Client or Script] --> B[FastAPI Service]
    B --> C[Device Routes]
    B --> D[Telemetry Routes]
    B --> E[Control Routes]
    D --> F[In-Memory Storage by Default]
    F --> G[History and Latest Queries]
    D -. optional .-> H[InfluxDB]
    D -. optional .-> I[TimescaleDB]
    J[Experimental Modules] -. not in default app .-> B
```

## Repository Layout

| Path | Role |
|---|---|
| `src/gridos/main.py` | Active FastAPI entry point for the reduced launch path |
| `src/gridos/api/` | API routes, dependency wiring, and WebSocket manager |
| `src/gridos/models/` | Small Pydantic model surface for the supported workflow |
| `src/gridos/storage/` | Storage interface plus optional persistent backends |
| `src/gridos/digital_twin/` | Experimental simulation and ML helpers kept outside the default runtime path |
| `docs/` | Documentation aligned to the current supported scope |
| `requirements/` | Reduced dependency groups for base, dev, ml, and production |
| `tests/` | Regression tests for the lightweight supported path |

## Quick Start

### Run from source

```bash
git clone https://github.com/iceccarelli/GridOS.git
cd GridOS
python -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"
cp .env.example .env
uvicorn gridos.main:app --host 0.0.0.0 --port 8000 --reload
```

Then open:

```text
http://localhost:8000/docs
```

### Run with Docker Compose

```bash
cp .env.example .env
docker compose up --build
```

Then open:

```text
http://localhost:8000/docs
```

## Live Device Connectivity (Modbus)

GridOS can talk to real DER hardware over Modbus TCP, and ships a built-in
device simulator so the full loop is demonstrable without hardware:

```bash
pip install -e ".[adapters]"

# Terminal 1 — emulate a battery on Modbus TCP:
gridos simulate --port 5020

# Terminal 2 — run the API, then drive the device over REST:
gridos serve
```

Register a device with an `adapter_config`, then connect, poll live telemetry,
and dispatch control:

```bash
BASE=http://127.0.0.1:8000/api/v1
curl -s -X POST $BASE/devices/register -H 'Content-Type: application/json' -d '{
  "device": {"device_id":"batt-1","name":"Battery","der_type":"battery","rated_power_kw":100},
  "adapter_config": {"protocol":"modbus_tcp","host":"127.0.0.1","port":5020}
}'
curl -s -X POST $BASE/devices/batt-1/connect      # attach + connect the adapter
curl -s -X POST $BASE/devices/batt-1/poll         # read LIVE telemetry, persist + broadcast it
curl -s -X POST $BASE/control/batt-1 -H 'Content-Type: application/json' \
     -d '{"device_id":"batt-1","mode":"power_setpoint","setpoint_kw":40}'
```

A complete scripted walkthrough (including a restart to prove device
persistence) lives at `scripts/demo_modbus.sh`.

### Register maps (SunSpec)

The Modbus adapter resolves a register map from `adapter_config["register_map"]`,
which may be a named preset or an inline custom map. Presets include
`gridos_default`, `sunspec_inverter`, and `sunspec_battery`:

```json
{"protocol": "modbus_tcp", "host": "127.0.0.1", "port": 5020, "register_map": "sunspec_inverter"}
```

The `sunspec_*` presets approximate the SunSpec information models for common
inverter/battery measurement points using fixed scale factors; full SunSpec
scale-factor decoding is a future enhancement, and inline maps cover exact
hardware. The simulator can emulate any map: `gridos simulate` seeds registers
from the chosen layout.

## Live Device Connectivity (MQTT)

For EV chargers and IoT gateways that *push* telemetry, GridOS ships an MQTT
adapter and a self-contained broker simulator:

```bash
# Terminal 1 — broker simulator that also publishes battery telemetry:
gridos simulate --protocol mqtt --port 1883

# Register an MQTT device and drive it the same way as Modbus:
curl -s -X POST $BASE/devices/register -H 'Content-Type: application/json' -d '{
  "device": {"device_id":"ev-1","name":"EV Charger","der_type":"ev_charger","rated_power_kw":11},
  "adapter_config": {"protocol":"mqtt","host":"127.0.0.1","port":1883,
                     "telemetry_topic":"gridos/mqtt-sim/telemetry"}
}'
curl -s -X POST $BASE/devices/ev-1/connect
curl -s -X POST $BASE/devices/ev-1/poll
```

For production, point the adapter at any MQTT broker (e.g. Mosquitto).

## Browser Demo

Run `gridos serve` and open **http://127.0.0.1:8000/demo** — a no-terminal page
that registers a device, connects its adapter, polls live telemetry (with an
auto-poll mode), and dispatches a setpoint, all from the browser. Start
`gridos simulate` alongside it and a prospect can watch the full loop without
touching a command line.

## Economic Dispatch & ROI

GridOS can answer the question a battery owner actually cares about: *how much
money does this asset make?* The economics engine solves a value-stacked MILP
(PuLP/CBC) that minimises the total electricity bill across three streams —
energy arbitrage, monthly demand-charge reduction, and throughput-based
degradation cost — then backtests the optimised schedule against a no-battery
baseline.

```bash
pip install -e ".[economics]"   # pulls the PuLP solver

# Representative-day demo (prints savings + payback):
PYTHONPATH=src python scripts/roi_demo.py

# Or over REST:
BASE=http://127.0.0.1:8000/api/v1
curl -s -X POST $BASE/economics/roi -H 'Content-Type: application/json' -d '{
  "load_kw": [50, 50, 120, 120, 60, 60, 50, 50],
  "solar_kw": [0, 0, 30, 40, 20, 10, 0, 0],
  "battery": {"capacity_kwh": 215, "max_discharge_kw": 100, "capex": 95000}
}'
```

The economics routes are mounted by default. Without the `[economics]` extra
they return a clear `503` with remediation rather than crashing the app.

> ROI figures are representative-day backtests under perfect foresight of load
> and solar — illustrative, not guarantees. A receding-horizon (MPC) controller
> and per-site interval data are the honest next steps before quoting a figure.

## Authentication

By default GridOS runs open, which suits local development and demos. To require
credentials on mutating endpoints, set two environment variables:

```bash
GRIDOS_REQUIRE_AUTH=true
GRIDOS_API_KEYS=gos_your_first_key,gos_your_second_key
```

When enabled, `POST /api/v1/devices/register`, `DELETE /api/v1/devices/{id}`,
`POST /api/v1/telemetry/`, `POST /api/v1/telemetry/batch`, and
`POST /api/v1/control/{id}` require a matching `X-API-Key` header. Read endpoints
remain open. Check the current posture at `GET /api/v1/auth/status`.

## Default Runtime Behavior

The default `.env.example` enables **in-memory storage**. That means the first run does not require any external database.

| Mode | Intended use |
|---|---|
| `GRIDOS_USE_INMEMORY_STORAGE=true` | Local development, demos, and validation |
| `GRIDOS_USE_INMEMORY_STORAGE=false` + `GRIDOS_STORAGE_BACKEND=sqlite` | Persistent local file storage, no external service |
| `GRIDOS_USE_INMEMORY_STORAGE=false` + InfluxDB config | Explicit persistent backend setup |
| `GRIDOS_USE_INMEMORY_STORAGE=false` + TimescaleDB config | Explicit persistent backend setup |

## Installation Targets

| Install target | Command |
|---|---|
| Core development path | `pip install -e ".[dev]"` |
| Add persistent storage drivers | `pip install -e ".[storage]"` |
| Add adapter extras | `pip install -e ".[adapters]"` |
| Add ML extras | `pip install -e ".[ml]"` |

## Testing

Protect the supported scope first.

```bash
pytest tests/test_api.py tests/test_models.py tests/test_storage.py -v --tb=short
```

## Documentation Guide

The most important docs for the current release are:

| Document | Purpose |
|---|---|
| `docs/quickstart.md` | Get a working local instance running |
| `docs/deployment.md` | Understand the supported deployment story |
| `docs/api_reference.md` | Confirm the active and optional API surface |
| `docs/models.md` | Understand the real model layer |
| `docs/adapters.md` | See the adapter framework boundaries |
| `docs/architecture.md` | See the reduced launch architecture |

## Contributing

Contributions are welcome, especially when they improve startup reliability, telemetry handling, testing, packaging, and documentation consistency. The priority is to strengthen the lightweight path before expanding the public scope again.

## License

GridOS is released under the [MIT License](LICENSE).
