📄 docs: Add readme
This commit is contained in:
@@ -0,0 +1,129 @@
|
|||||||
|
# ESP32 "CYD" ThermoPro Bridge
|
||||||
|
|
||||||
|
A proof-of-concept bridge that reads temperature data from a **ThermoPro TP930** (among others) Bluetooth BBQ thermometer and ships it to a self-hosted HTTP API for storage and display.
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
```plain
|
||||||
|
ThermoPro TP930 (BLE)
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
thermopro_cli.py ←── BLE reader (reverse-engineered protocol, Linux only)
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
bridge_poc.py ←── polls CLI, normalizes payload, POSTs to API
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
receiver.php ←── stores latest reading + NDJSON log, serves status page
|
||||||
|
```
|
||||||
|
|
||||||
|
## Components
|
||||||
|
|
||||||
|
### `thermopro_cli.py`
|
||||||
|
|
||||||
|
Local copy of the [thermopro-cli](thermopro-cli/) tool. Communicates directly with the ThermoPro thermometer over BLE using a reverse-engineered protocol. Outputs probe temperatures, battery level, and unit as JSON.
|
||||||
|
|
||||||
|
### `bridge_poc.py`
|
||||||
|
|
||||||
|
Polling bridge that:
|
||||||
|
|
||||||
|
1. Calls `thermopro_cli.py` on a configurable interval
|
||||||
|
2. Normalizes the raw CLI output into a structured payload
|
||||||
|
3. POSTs the reading to the configured HTTP endpoint with a shared secret token
|
||||||
|
|
||||||
|
### `receiver.php`
|
||||||
|
|
||||||
|
Minimal PHP API server that:
|
||||||
|
|
||||||
|
- `POST /api/thermopro/readings` — accepts and validates a reading, writes it to `data/latest.json` and appends to `data/readings.ndjson`
|
||||||
|
- `GET /api/thermopro/latest` — returns the most recent reading as JSON
|
||||||
|
- `GET /` or `GET /status` — HTML status page (auto-refreshes every 10 seconds)
|
||||||
|
|
||||||
|
## Data Format
|
||||||
|
|
||||||
|
Each reading payload looks like:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"deviceId": "tp930-smoker",
|
||||||
|
"device": "ThermoPro TP930",
|
||||||
|
"mac": "C9:48:1D:B1:E1:E7",
|
||||||
|
"connected": true,
|
||||||
|
"battery": 90,
|
||||||
|
"unit": "F",
|
||||||
|
"probeCount": 4,
|
||||||
|
"probes": [
|
||||||
|
{ "id": 1, "name": "Probe 1", "temperature": 266.2, "connected": true },
|
||||||
|
{ "id": 2, "name": "Probe 2", "temperature": null, "connected": false },
|
||||||
|
{ "id": 3, "name": "Probe 3", "temperature": null, "connected": false },
|
||||||
|
{ "id": 4, "name": "Probe 4", "temperature": null, "connected": false }
|
||||||
|
],
|
||||||
|
"readingTime": "2026-06-06T12:00:00+00:00",
|
||||||
|
"bridgeTime": "2026-06-06T12:00:01+00:00",
|
||||||
|
"source": "thermopro_cli"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
- **Bridge host:** Linux with Bluetooth (BlueZ), Python 3.8+, `bleak` library
|
||||||
|
- **API server:** PHP 8.x with write access to the `data/` directory
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
### 1. Install Python dependencies
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip install bleak
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Configure the receiver
|
||||||
|
|
||||||
|
Deploy `receiver.php` to a PHP-capable web server. Set the `THERMOPRO_BRIDGE_TOKEN` environment variable (or it defaults to `dev-secret`):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export THERMOPRO_BRIDGE_TOKEN="your-secret-token"
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Run the bridge
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python bridge_poc.py \
|
||||||
|
--cli thermopro_cli.py \
|
||||||
|
--endpoint https://your-server/api/thermopro/readings \
|
||||||
|
--token your-secret-token \
|
||||||
|
--device-id tp930-smoker \
|
||||||
|
--mac C9:48:1D:B1:E1:E7 \
|
||||||
|
--interval 15
|
||||||
|
```
|
||||||
|
|
||||||
|
**Options:**
|
||||||
|
|
||||||
|
| Flag | Default | Description |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `--cli` | `thermopro_cli.py` | Path to `thermopro_cli.py` |
|
||||||
|
| `--endpoint` | `http://127.0.0.1:8000/api/thermopro/readings` | API endpoint |
|
||||||
|
| `--token` | `dev-secret` | Shared token (`X-Bridge-Token` header) |
|
||||||
|
| `--device-id` | `tp930-smoker` | Stable identifier for this device |
|
||||||
|
| `--mac` | `C9:48:1D:B1:E1:E7` | ThermoPro BLE MAC address |
|
||||||
|
| `--interval` | `15` | Polling interval in seconds |
|
||||||
|
| `--once` | — | Read and POST once, then exit |
|
||||||
|
|
||||||
|
### Finding your device's MAC address
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python thermopro_cli.py scan
|
||||||
|
```
|
||||||
|
|
||||||
|
## Data Storage
|
||||||
|
|
||||||
|
| File | Contents |
|
||||||
|
| --- | --- |
|
||||||
|
| `data/latest.json` | Most recent reading (overwritten each poll) |
|
||||||
|
| `data/readings.ndjson` | Append-only log of all readings (newline-delimited JSON) |
|
||||||
|
|
||||||
|
## Security
|
||||||
|
|
||||||
|
- The API endpoint is protected by a shared secret sent as the `X-Bridge-Token` request header.
|
||||||
|
- Use HTTPS for the endpoint in production to keep the token confidential.
|
||||||
|
- The token is compared with `hash_equals()` to prevent timing attacks.
|
||||||
Reference in New Issue
Block a user