https://github.com/braboj/demo-sensor-app
Multi-service demo simulating industrial plant sensor readings: Flask REST API, Angular SPA, PostgreSQL, and Grafana — orchestrated with Docker Compose.
https://github.com/braboj/demo-sensor-app
angular dashboard demo docker docker-compose flask grafana gunicorn iot postgresql python rest-api sensor-data sqlalchemy typescript
Last synced: about 10 hours ago
JSON representation
Multi-service demo simulating industrial plant sensor readings: Flask REST API, Angular SPA, PostgreSQL, and Grafana — orchestrated with Docker Compose.
- Host: GitHub
- URL: https://github.com/braboj/demo-sensor-app
- Owner: braboj
- License: mit
- Created: 2025-01-23T14:21:21.000Z (over 1 year ago)
- Default Branch: main
- Last Pushed: 2026-07-04T10:22:17.000Z (8 days ago)
- Last Synced: 2026-07-04T10:24:39.906Z (8 days ago)
- Topics: angular, dashboard, demo, docker, docker-compose, flask, grafana, gunicorn, iot, postgresql, python, rest-api, sensor-data, sqlalchemy, typescript
- Language: Python
- Homepage: https://sensor-app-frontend.onrender.com/
- Size: 865 KB
- Stars: 2
- Watchers: 1
- Forks: 0
- Open Issues: 20
-
Metadata Files:
- Readme: README.md
- Contributing: CONTRIBUTING.md
- License: LICENSE
Awesome Lists containing this project
README
[](https://github.com/braboj/demo-sensor-app/actions/workflows/ci.yml)
[](https://github.com/braboj/demo-sensor-app/actions/workflows/codeql.yml)
[](LICENSE)
# Sensor Dashboard
*A multi-service demo that simulates industrial-plant sensor readings —
generated, stored, streamed live, and charted.*
A Python (Flask) backend generates sensor data via a standalone worker, stores
it in PostgreSQL, and exposes it through a versioned REST API; an Angular
frontend shows a live table and embeds Grafana for charting.
**🌐 Live demo:** — the **Live Table**
tab streams new readings in real time, and the **Charts** tab embeds the Grafana
dashboard. It runs on Render's free tier, so the first request after a while may
cold-start for ~30–60s.
## Features
- **Live table** — Angular SPA (standalone components) with explicit loading,
empty, and error states; new readings arrive over **Server-Sent Events**
- **Charts** — the embedded Grafana "Sensor Readings" dashboard with a metric
selector (temperature / humidity / vibration) and a moving-average trend,
provisioned as code.
- **Versioned REST API** — `/api/v1/sensors` with a bounded `?limit=`, ISO-8601
UTC timestamps, and RFC 9457 JSON errors; plus `/health` and `/ready`.
- **Sensor Simulator** — the simulator runs as its own worker process, not a
thread inside the web app.
- **Deployable** — a free-tier [Render blueprint](render.yaml) runs the whole
stack (see [Deploy](docs/DEPLOY.md)).
## Quick start
Prerequisites:
- [Docker](https://docs.docker.com/engine/install/)
- [git](https://git-scm.com/downloads)
Clone the repository and start the full stack:
```bash
git clone https://github.com/braboj/demo-sensor-app
cd demo-sensor-app
docker compose up
```
This starts the frontend, backend, a PostgreSQL database, a one-shot migration
step, the data-generator worker, and Grafana:
- [Frontend (Angular) / localhost:4200](http://localhost:4200)
- [Backend (Flask) / localhost:5000](http://localhost:5000)
- [Grafana / localhost:3000](http://localhost:3000)
Grafana ships with a PostgreSQL datasource and the "Sensor Readings" dashboard
provisioned as code (`deploy/grafana/provisioning/`). The admin login is set via
the `GRAFANA_ADMIN_USER` / `GRAFANA_ADMIN_PASSWORD` environment variables (see
`docker-compose.yml`) — set your own before exposing Grafana beyond local use.
## Usage
Fetch the most recent readings (the last 100 by default):
```bash
curl http://localhost:5000/api/v1/sensors
```
```json
[
{
"id": 1940,
"timestamp": "2026-07-01T06:27:14.147279+00:00",
"temperature": 4.4,
"humidity": 93.14,
"vibration": 0
},
{
"id": 1939,
"timestamp": "2026-07-01T06:27:04.139090+00:00",
"temperature": 11.32,
"humidity": 92.68,
"vibration": 1
}
]
```
Vibration is a coded value: `0` = none, `1` = detected.
Bound the result with `?limit=` (an integer 1–100; a non-integer or
out-of-range value returns `400`, never a silent fallback):
```bash
curl "http://localhost:5000/api/v1/sensors?limit=1"
```
Subscribe to the live stream (Server-Sent Events) — new readings arrive as they
are recorded (see [ADR-0005](docs/decisions/0005-realtime-delivery-sse.md)):
```bash
curl -N http://localhost:5000/api/v1/sensors/stream
```
Operational endpoints: `GET /health` (liveness — 200 if the process is up) and
`GET /ready` (readiness — 200 if the database is reachable, else 503).
## Project structure
| Path | Purpose |
|------|---------|
| `backend/` | Flask REST API + data-generator worker |
| `backend/src/sensor_api/` | App factory, config, blueprints (sensors, health), worker |
| `backend/migrations/` | Alembic migrations |
| `backend/tests/` | pytest suite |
| `frontend/` | Angular SPA (standalone components) |
| `frontend/src/app/home/` | Live readings table + SSE |
| `frontend/src/app/charts/` | Embedded Grafana dashboard |
| `frontend/src/environments/` | API / Grafana URLs (dev + prod) |
| `deploy/grafana/` | Grafana image + datasource/dashboard provisioned as code |
| `docs/` | ONBOARDING, PLAYBOOK, DEPLOY, decisions (ADRs), history |
| `docs/arc42/` | Architecture documentation (arc42) |
| `docker-compose.yml` | Full local stack (backend, frontend, db, grafana, worker) |
| `render.yaml` | Render free-tier blueprint |
## Development
`docker compose up` (above) runs the whole stack. To work on a service on its
own — PostgreSQL is the only external dependency (`docker compose up db` starts
one):
Backend (Python 3.12, from `backend/`):
```bash
pip install -r requirements.txt
pytest && mypy src --strict # tests + strict type check
flask --app sensor_api run # dev server (needs a reachable PostgreSQL)
python -m sensor_api.worker # run the data generator
```
Frontend (Node 22, from `frontend/`):
```bash
npm ci
npm start # ng serve on :4200
npm test && npx eslint . # unit tests + lint
```
The backend reads its configuration from environment variables — copy the
committed `backend/.env.example` to `backend/.env` and adjust. See
[Onboarding](docs/ONBOARDING.md) for full setup and [Playbook](docs/PLAYBOOK.md)
for day-to-day commands.
## Configuration
Services are configured through environment variables (documented in
`backend/.env.example` and [`docker-compose.yml`](docker-compose.yml)); the most
important:
| Variable | Service | Default | Description |
|----------|---------|---------|-------------|
| `DATABASE_URL` | backend, grafana | — | PostgreSQL connection string. |
| `SECRET_KEY` | backend | — | Flask secret key (**sensitive** — set your own). |
| `CORS_ORIGINS` | backend | — | Allowed frontend origin (never `*`). |
| `APP_CONFIG` | backend | `production` | Config profile (`development` / `production`). |
| `SAMPLE_INTERVAL_SECONDS` | backend, worker | `10` | Generator sample interval. |
| `LOG_LEVEL` | backend, worker | `INFO` | Log level for the structured JSON logs (never `DEBUG` in prod). |
| `RUN_INPROCESS_GENERATOR` | backend | `false` | Run the generator in-process (free-tier only). |
| `WEB_CONCURRENCY` | backend | `1` | gunicorn worker count. |
| `API_URL` | frontend (build) | same-origin `/api/v1/sensors` | Absolute backend URL baked into the bundle. |
| `GRAFANA_URL` | frontend (build) | empty | Grafana dashboard URL for the Charts view. |
| `GRAFANA_ADMIN_USER` / `GRAFANA_ADMIN_PASSWORD` | grafana | — | Local admin login (**sensitive**). |
See [Deploy](docs/DEPLOY.md) for the full deployment variable set.
## Deploy
The repo ships a [`render.yaml`](render.yaml) blueprint that runs the whole
stack on Render's free tier — managed Postgres, the Dockerized backend, the
static frontend, and Grafana. The live demo above runs from this blueprint. See
[Deploy](docs/DEPLOY.md) for the walkthrough and the free-tier caveats.
## Next steps
- To understand the architecture, see the [Architecture docs (arc42)](docs/arc42/README.md)
- To set up a dev environment, see [Onboarding](docs/ONBOARDING.md)
- For day-to-day commands, see the [Playbook](docs/PLAYBOOK.md)
- To deploy on Render (free tier), see [Deploy](docs/DEPLOY.md)
- To learn more about the project, see the [Assignment](docs/history/ASSIGNMENT.md)
- To read about the solution, see the [Solution](docs/history/SOLUTION.md)
- To contribute, see [Contributing](CONTRIBUTING.md)
- To leave feedback, visit [Discussions](https://github.com/braboj/demo-sensor-app/discussions)
## License
Licensed under the [MIT License](LICENSE).