Initial commit: Waste Collection API

This commit is contained in:
2026-04-05 07:29:36 +00:00
commit 109b682a88
17 changed files with 1016 additions and 0 deletions

247
SPEC.md Normal file
View File

@@ -0,0 +1,247 @@
# Waste Collection API — OpenAPI Specification
**Base URL:** `http://localhost:8000`
**Version:** 1.0.0
**Purpose:** Return waste collection schedules for German municipalities via push notification targets (email, Telegram, etc.)
---
## API Endpoints
### `GET /health`
Health check.
**Response `200`**
```json
{ "status": "ok" }
```
---
### `GET /sources`
List all available source IDs (e.g. `abfall_io`, `awm_muenchen_de`, `ics`).
**Response `200`**
```json
{
"count": 3,
"sources": [
{ "id": "ics", "name": "Generic ICS / iCal URL" },
{ "id": "abfall_io", "name": "Abfall.IO (Germany)" },
{ "id": "awm_muenchen_de","name": "AWM München (Germany)" }
]
}
```
---
### `GET /sources/{source_id}`
Describe required params for a given source.
**Path params:**
`source_id` — e.g. `ics`, `abfall_io`, `awm_muenchen_de`
**Response `200`**
```json
{
"id": "abfall_io",
"name": "Abfall.IO (Germany)",
"params": [
{ "name": "key", "type": "string", "required": true, "example": "8215c62763967916979e0e8566b6172e" },
{ "name": "f_id_kommune","type": "integer","required": true, "example": 2999 },
{ "name": "f_id_strasse","type": "integer","required": true, "example": 1087 }
]
}
```
---
### `POST /collections/ics`
Fetch waste collections from a generic ICS/iCal URL.
**Request body**
```json
{
"url": "https://example.com/kalender.ics",
"count": 10
}
```
| Field | Type | Required | Default | Description |
|-------|------|----------|---------|-------------|
| `url` | string | ✅ | — | Direct URL to `.ics` file |
| `count` | integer | ❌ | 20 | Max entries to return |
**Response `200`**
```json
{
"source": "ics",
"url": "https://example.com/kalender.ics",
"collections": [
{
"date": "2026-04-08",
"type": "Restmülltonne",
"daysUntil": 4,
"icon": "mdi:trash-can"
},
{
"date": "2026-04-27",
"type": "Papiertonne",
"daysUntil": 23,
"icon": "mdi:paper-bin"
}
]
}
```
---
### `POST /collections/abfall_io`
Fetch collections via Abfall.IO (many German municipalities).
**Request body**
```json
{
"key": "8215c62763967916979e0e8566b6172e",
"f_id_kommune": 2999,
"f_id_strasse": 1087,
"count": 20
}
```
**Response `200`** — same shape as `/collections/ics` with `source: "abfall_io"`.
---
### `POST /collections/awm_muenchen_de`
Fetch collections for Munich via AWM.
**Request body**
```json
{
"street": "Bahnstr.",
"house_number": "11",
"count": 20
}
```
**Response `200`** — same shape as `/collections/ics` with `source: "awm_muenchen_de"`.
---
### `POST /notify`
Queue a notification for a future collection.
**Request body**
```json
{
"source_id": "ics",
"collection": {
"date": "2026-04-08",
"type": "Restmülltonne",
"daysUntil": 4,
"icon": "mdi:trash-can"
},
"notify_at_days_before": 1,
"channels": ["telegram"],
"chat_id": 1320170074
}
```
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `source_id` | string | ✅ | Source that returned this collection |
| `collection` | object | ✅ | A single collection entry |
| `notify_at_days_before` | integer | ❌ | Days before collection to fire (default: 1) |
| `channels` | string[] | ✅ | `["telegram"]` (more coming) |
| `chat_id` | integer | ✅ | Telegram chat ID to notify |
**Response `200`**
```json
{
"ok": true,
"notification_id": "notif_abc123",
"message": "Reminder set for 2026-04-08: Restmülltonne"
}
```
---
### `GET /notifications`
List all active notification rules.
**Response `200`**
```json
{
"count": 1,
"notifications": [
{
"id": "notif_abc123",
"source_id": "ics",
"collection_type": "Restmülltonne",
"collection_date": "2026-04-08",
"notify_at_days_before": 1,
"channels": ["telegram"],
"chat_id": 1320170074,
"fired": false
}
]
}
```
---
### `DELETE /notifications/{notification_id}`
Delete a notification rule.
**Response `200`**
```json
{ "ok": true, "deleted": "notif_abc123" }
```
---
## Error Responses
All endpoints return errors in this shape:
```json
{
"error": "Human-readable error message",
"detail": "Optional technical detail"
}
```
| Status | Meaning |
|--------|---------|
| `400` | Bad request — missing or invalid params |
| `404` | Source not found |
| `422` | Unprocessable entity — source returned no data |
| `500` | Internal server error |
---
## Data Models
### `Collection`
```json
{
"date": "YYYY-MM-DD",
"type": "Human-readable waste type string",
"daysUntil": 4,
"icon": "mdi:icon-name"
}
```
### `Notification`
```json
{
"id": "notif_abc123",
"source_id": "ics | abfall_io | awm_muenchen_de",
"collection_type": "Restmülltonne",
"collection_date": "2026-04-08",
"notify_at_days_before": 1,
"channels": ["telegram"],
"chat_id": 1320170074,
"fired": false
}
```