diff --git a/README.md b/README.md index 2518764..3f54589 100644 --- a/README.md +++ b/README.md @@ -9,582 +9,41 @@ --- -## ๐Ÿ“‹ Table of Contents - -- [Hardware](#-hardware) -- [Architecture](#-architecture) -- [Installation](#-installation) -- [Configuration](#๏ธ-configuration) -- [Usage](#-usage) - - [CLI](#cli) - - [HTTP API](#http-api) - - [Docker](#docker) -- [API Reference](#-api-reference) -- [Home Assistant Integration](#-home-assistant-integration) -- [Development](#-development) -- [Project Structure](#-project-structure) - ---- - -## ๐Ÿ”Œ Hardware - -| Component | Details | -|-----------|---------| -| **Sensor** | Bosch BME280 โ€” Waveshare Environmental Sensor (SKU 15231) | -| **Board** | Raspberry Pi Rev 2+ (IยฒC bus 1) | -| **Interface** | IยฒC โ€” default address `0x77`, alternate `0x76` (SDO pin) | -| **Protocol** | SMBus via `/dev/i2c-1` | - -### Wiring (Raspberry Pi GPIO) - -``` -BME280 Raspberry Pi -โ”€โ”€โ”€โ”€โ”€โ”€ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ -VCC โ”€โ”€โ”€โ”€โ”€โ”€โ–บ Pin 1 (3.3V) -GND โ”€โ”€โ”€โ”€โ”€โ”€โ–บ Pin 6 (GND) -SDA โ”€โ”€โ”€โ”€โ”€โ”€โ–บ Pin 3 (GPIO2 / SDA1) -SCL โ”€โ”€โ”€โ”€โ”€โ”€โ–บ Pin 5 (GPIO3 / SCL1) -``` - -> ๐Ÿ’ก Make sure IยฒC is enabled: `sudo raspi-config` โ†’ Interface Options โ†’ I2C โ†’ Enable - ---- - -## ๐Ÿ—๏ธ Architecture - -``` -โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” -โ”‚ Raspberry Pi โ”‚ -โ”‚ โ”‚ -โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ -โ”‚ โ”‚ bme280.py โ”‚ โ”‚ sensor_api.py โ”‚ โ”‚ -โ”‚ โ”‚ โ”‚ โ”‚ (Flask) โ”‚ โ”‚ -โ”‚ โ”‚ IยฒC driver โ”‚โ—„โ”€โ”€โ”€โ”€โ”‚ GET /bme280 โ”‚ โ”‚ -โ”‚ โ”‚ Calibration โ”‚ โ”‚ GET /bme280/publish โ”‚ โ”‚ -โ”‚ โ”‚ algorithms โ”‚ โ”‚ GET /health โ”‚ โ”‚ -โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ -โ”‚ โ”‚ โ”‚ โ”‚ -โ”‚ โ–ผ โ–ผ โ”‚ -โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ -โ”‚ โ”‚ BME280 chip โ”‚ โ”‚ MQTT Broker โ”‚ โ”‚ -โ”‚ โ”‚ (IยฒC 0x77) โ”‚ โ”‚ (Home Assistant) โ”‚ โ”‚ -โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ -โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ -``` - ---- - -## ๐Ÿ“ฆ Installation - -### Prerequisites - -```bash -# Enable IยฒC on Raspberry Pi -sudo raspi-config # Interface Options โ†’ I2C โ†’ Enable -sudo reboot -``` - -### Clone & install +## Quick start ```bash git clone https://github.com/guillaumedelre/bme280.git cd bme280 pip install -r requirements.txt -``` +cp .env.example .env # fill in your MQTT broker details -### Configure - -```bash -cp .env.example .env -# Edit .env with your MQTT broker details -nano .env -``` - ---- - -## โš™๏ธ Configuration - -All settings are driven by environment variables. Copy `.env.example` to `.env` and fill in your values. - -| Variable | Default | Description | -|----------|---------|-------------| -| `BME280_I2C_BUS` | `1` | IยฒC bus number (`1` for Pi Rev 2+, `0` for Rev 1) | -| `BME280_I2C_ADDRESS` | `0x77` | Sensor IยฒC address (`0x77` or `0x76` depending on SDO pin) | -| `BME280_IIR_FILTER` | `0` | IIR filter coefficient: `0`=off, `1`=2ร—, `2`=4ร—, `3`=8ร—, `4`=16ร— (recommended: `2` for indoor use) | -| `MQTT_BROKER_HOST` | `localhost` | IP or hostname of your MQTT broker | -| `MQTT_USERNAME` | _(empty)_ | MQTT username (leave empty for anonymous) | -| `MQTT_PASSWORD` | _(empty)_ | MQTT password | -| `MQTT_CLIENT_ID` | `rpi-bme280` | MQTT client identifier | -| `FLASK_PORT` | `5000` | HTTP port for the API | -| `FLASK_DEBUG` | `false` | Enable Flask debug mode (`true` / `false`) | - -**Example `.env`:** - -```ini -BME280_I2C_BUS=1 -BME280_I2C_ADDRESS=0x77 -BME280_IIR_FILTER=0 - -MQTT_BROKER_HOST=192.168.1.10 -MQTT_USERNAME=homeassistant -MQTT_PASSWORD=your_password_here -MQTT_CLIENT_ID=rpi-bme280 - -FLASK_PORT=5000 -FLASK_DEBUG=false +python sensor_api.py # API available at http://0.0.0.0:5000 ``` ---- - -## ๐Ÿš€ Usage - -### CLI - -Read sensor data directly from the terminal (requires hardware): - -```bash -python bme280.py -``` - -``` -Chip ID : 96 -Version : 0 -Temperature : 21.55 ยฐC -Pressure : 1005.16 hPa -Humidity : 44.57 %RH -``` - -Use an alternate IยฒC address: - ```bash -BME280_I2C_ADDRESS=0x76 python bme280.py -``` - -### HTTP API - -Start the API server: - -```bash -python sensor_api.py -# Listening on http://0.0.0.0:5000 -``` - -```bash -# Health check -curl http://rpi.local:5000/health - -# Read sensor data -curl http://rpi.local:5000/bme280 | python -m json.tool - -# Publish to MQTT +curl http://rpi.local:5000/bme280 curl http://rpi.local:5000/bme280/publish ``` -#### Automate with cron - -Publish sensor data every minute via cron: - -```bash -crontab -e -``` - -```cron -* * * * * curl -s http://localhost:5000/bme280/publish >> /var/log/bme280.log 2>&1 -``` - -### Docker - -#### Run the API in Docker (Raspberry Pi) - -```bash -# Build the app image -docker build --target app -t bme280-app . - -# Run with IยฒC device passthrough and env file -docker run -d \ - --name bme280 \ - --device /dev/i2c-1:/dev/i2c-1 \ - --env-file .env \ - -p 5000:5000 \ - bme280-app -``` - -Or with docker compose: - -```bash -# Edit docker-compose.yml and uncomment the devices section -docker compose up app -``` - -#### Run tests in Docker (no hardware required) - -```bash -docker compose run --rm test -# or -docker build --target test -t bme280-test . && docker run --rm bme280-test -``` - ---- - -## ๐Ÿ“ก API Reference - -### `GET /health` - -Service health check. - -**Response `200`:** -```json -{ - "status": "ok" -} -``` - ---- - -### `GET /` - -Root endpoint. - -**Response `200`:** -```json -{} -``` - ---- - -### `GET /bme280` - -Returns current sensor readings with full device metadata. - -**Response `200`:** -```json -{ - "name": "bme280", - "brand": "Waveshare", - "part_number": "BME280 Environmental Sensor", - "sku": 15231, - "upc": 614961952638, - "chip": { - "id": 96, - "version": 0 - }, - "capabilities": { - "temperature": { - "unit_of_measurement": "ยฐC", - "min": -40, - "max": 85, - "resolution": 0.01, - "accuracy": 1 - }, - "humidity": { - "unit_of_measurement": "%RH", - "min": 0, - "max": 100, - "resolution": 0.008, - "accuracy": 3 - }, - "pressure": { - "unit_of_measurement": "hPa", - "min": 300, - "max": 1100, - "resolution": 0.008, - "accuracy": 0.0018 - } - }, - "data": { - "temperature": 21.55, - "humidity": 44.57, - "pressure": 1005.16 - } -} -``` - -**Response `503`** โ€” sensor unavailable (IยฒC error): -```json -{ - "error": "Sensor unavailable", - "detail": "[Errno 2] No such file or directory: '/dev/i2c-1'" -} -``` - ---- - -### `GET /bme280/publish` - -Reads sensor data and publishes each measurement to the configured MQTT broker. - -**MQTT topics published:** - -| Topic | Payload | Example | -|-------|---------|---------| -| `sensor/bme280_temperature` | float string | `21.55` | -| `sensor/bme280_humidity` | float string | `44.57` | -| `sensor/bme280_pressure` | float string | `1005.16` | - -**Response `200`:** -```json -{ - "published": true, - "topics": [ - "sensor/bme280_temperature", - "sensor/bme280_humidity", - "sensor/bme280_pressure" - ] -} -``` - -**Response `503`** โ€” sensor unavailable: -```json -{ - "error": "Sensor unavailable", - "detail": "..." -} -``` - -**Response `502`** โ€” MQTT broker unreachable: -```json -{ - "error": "MQTT publish failed", - "detail": "Connection refused" -} -``` - ---- - -## ๐Ÿ  Home Assistant Integration - -### How it works - -``` -BME280 sensor - โ”‚ IยฒC - โ–ผ -bme280.py (driver) - โ”‚ temperature / pressure / humidity - โ–ผ -sensor_api.py (Flask) - โ”‚ GET /bme280/publish - โ–ผ -MQTT broker (Mosquitto) โ—„โ”€โ”€ Home Assistant polls topics - โ”‚ - โ”œโ”€โ”€ sensor/bme280_temperature โ†’ 21.55 - โ”œโ”€โ”€ sensor/bme280_humidity โ†’ 44.57 - โ””โ”€โ”€ sensor/bme280_pressure โ†’ 1005.16 - โ”‚ - โ–ผ - Home Assistant dashboard -``` - -Every call to `/bme280/publish` (manually or via cron) pushes the three values to the broker. Home Assistant reads them in real time and updates the entity states. - ---- - -### Prerequisites - -#### 1. Install Mosquitto in Home Assistant - -In HA: **Settings โ†’ Add-ons โ†’ Mosquitto broker** โ†’ Install โ†’ Start. - -> ๐Ÿ’ก Enable "Start on boot" to survive reboots. - -#### 2. Create a dedicated MQTT user - -**Settings โ†’ People โ†’ Users** โ†’ Add user (e.g. `rpi-bme280`). This user will be used by the Raspberry Pi to publish. - -#### 3. Enable the MQTT integration - -**Settings โ†’ Devices & Services โ†’ Add integration โ†’ MQTT** โ†’ point to `localhost:1883` with the user created above. - -#### 4. Get your credentials - -Fill your `.env` on the Pi with the values from the steps above: - -```ini -MQTT_BROKER_HOST=192.168.1.10 # HA host IP -MQTT_USERNAME=rpi-bme280 -MQTT_PASSWORD=your_password -``` - -> ๐Ÿ”’ Never commit `.env` โ€” it is listed in `.gitignore`. - ---- - -### Option A โ€” MQTT auto-discovery (recommended) - -Home Assistant supports [MQTT discovery][ha-mqtt-discovery]: publish a JSON config payload once and the entity appears automatically in the UI โ€” no `configuration.yaml` edit needed. - -Run this once from the Pi (replace `` and credentials): - -```bash -mosquitto_pub -h -u rpi-bme280 -P \ - -t "homeassistant/sensor/bme280_temperature/config" \ - -m '{"name":"BME280 Temperature","state_topic":"sensor/bme280_temperature","unit_of_measurement":"ยฐC","device_class":"temperature","state_class":"measurement","unique_id":"bme280_temperature"}' - -mosquitto_pub -h -u rpi-bme280 -P \ - -t "homeassistant/sensor/bme280_humidity/config" \ - -m '{"name":"BME280 Humidity","state_topic":"sensor/bme280_humidity","unit_of_measurement":"%","device_class":"humidity","state_class":"measurement","unique_id":"bme280_humidity"}' - -mosquitto_pub -h -u rpi-bme280 -P \ - -t "homeassistant/sensor/bme280_pressure/config" \ - -m '{"name":"BME280 Pressure","state_topic":"sensor/bme280_pressure","unit_of_measurement":"hPa","device_class":"atmospheric_pressure","state_class":"measurement","unique_id":"bme280_pressure"}' -``` - -The three entities appear under **Settings โ†’ Devices & Services โ†’ MQTT** within seconds. - ---- - -### Option B โ€” Manual MQTT sensors - -If you prefer explicit config, add to your `configuration.yaml`: - -```yaml -mqtt: - sensor: - - name: "BME280 Temperature" - state_topic: "sensor/bme280_temperature" - unit_of_measurement: "ยฐC" - device_class: temperature - state_class: measurement - - - name: "BME280 Humidity" - state_topic: "sensor/bme280_humidity" - unit_of_measurement: "%" - device_class: humidity - state_class: measurement - - - name: "BME280 Pressure" - state_topic: "sensor/bme280_pressure" - unit_of_measurement: "hPa" - device_class: atmospheric_pressure - state_class: measurement -``` - -Then: **Developer Tools โ†’ YAML โ†’ Check configuration โ†’ Restart**. - ---- - -### Automation example - -Trigger an alert when humidity exceeds 70%: - -```yaml -automation: - - alias: "High humidity alert" - trigger: - - platform: numeric_state - entity_id: sensor.bme280_humidity - above: 70 - action: - - service: notify.mobile_app - data: - message: "โš ๏ธ Humidity is {{ states('sensor.bme280_humidity') }}%" -``` - ---- - -### Troubleshooting - -| Symptom | Check | -|---------|-------| -| Entity stuck at `unavailable` | Verify cron is running: `crontab -l` | -| `502` on `/bme280/publish` | Broker unreachable โ€” check `MQTT_BROKER_HOST` and broker is up | -| No entity in HA after discovery | Check discovery is enabled in MQTT integration settings | -| Wrong values | Confirm `BME280_I2C_ADDRESS` matches your wiring (`0x76` vs `0x77`) | - ---- - -## ๐Ÿ› ๏ธ Development - -### Install dev dependencies - -```bash -pip install -r requirements-dev.txt -``` - -### Run the test suite - -```bash -pytest tests/ -v -``` - -``` -tests/test_api.py::test_health_returns_ok PASSED -tests/test_api.py::test_index_returns_empty_json PASSED -tests/test_api.py::test_bme280_returns_sensor_data PASSED -tests/test_api.py::test_bme280_returns_503_when_sensor_unavailable PASSED -tests/test_api.py::test_publish_returns_200_with_topics PASSED -tests/test_api.py::test_publish_returns_503_when_sensor_unavailable PASSED -tests/test_api.py::test_publish_returns_502_when_mqtt_fails PASSED -tests/test_bme280.py::test_get_short_positive PASSED -tests/test_bme280.py::test_get_short_negative PASSED -tests/test_bme280.py::test_get_ushort PASSED -tests/test_bme280.py::test_get_char_positive PASSED -tests/test_bme280.py::test_get_char_negative PASSED -tests/test_bme280.py::test_get_uchar PASSED -tests/test_bme280.py::test_read_id_returns_chip_id_and_version PASSED -tests/test_bme280.py::test_read_id_custom_chip_id PASSED -tests/test_bme280.py::test_sensor_returns_required_keys PASSED -tests/test_bme280.py::test_sensor_data_has_three_measurements PASSED -tests/test_bme280.py::test_humidity_clamped_within_range PASSED -tests/test_bme280.py::test_pressure_zero_when_p1_calibration_is_zero PASSED - -19 passed in 0.22s -``` - -> ๐Ÿ’ก All tests run without physical hardware โ€” `smbus2` is fully mocked via `unittest.mock`. - -### Run tests in Docker - -```bash -docker compose run --rm test -``` - ---- - -## ๐Ÿ“ Project Structure - -``` -bme280/ -โ”œโ”€โ”€ ๐Ÿ“„ bme280.py # IยฒC driver โ€” Bosch calibration algorithms -โ”œโ”€โ”€ ๐Ÿ“„ sensor_api.py # Flask HTTP API + MQTT publisher -โ”‚ -โ”œโ”€โ”€ ๐Ÿงช tests/ -โ”‚ โ”œโ”€โ”€ test_bme280.py # Driver unit tests (hardware mocked) -โ”‚ โ””โ”€โ”€ test_api.py # API route tests (Flask test client) -โ”‚ -โ”œโ”€โ”€ ๐Ÿณ Dockerfile # Multi-stage: test / app -โ”œโ”€โ”€ ๐Ÿณ docker-compose.yml # test + app services -โ”‚ -โ”œโ”€โ”€ โš™๏ธ .env.example # Configuration template -โ”œโ”€โ”€ ๐Ÿ“‹ requirements.txt # Runtime dependencies -โ”œโ”€โ”€ ๐Ÿ“‹ requirements-dev.txt # Dev/test dependencies -โ”œโ”€โ”€ ๐Ÿ”ง pytest.ini # Pytest configuration -โ””โ”€โ”€ ๐Ÿ”’ .gitignore -``` - -### Key dependencies - -| Package | Version | Role | -|---------|---------|------| -| `smbus2` | โ‰ฅ 0.4.3 | IยฒC communication | -| `flask` | โ‰ฅ 3.0.0 | HTTP API framework | -| `paho-mqtt` | โ‰ฅ 1.6.1 | MQTT client | -| `python-dotenv` | โ‰ฅ 1.0.0 | `.env` file loading | - --- -## ๐Ÿ“Š Sensor Specifications +## Documentation -| Measurement | Range | Resolution | Accuracy | -|-------------|-------|------------|----------| -| ๐ŸŒก๏ธ Temperature | -40 ยฐC โ†’ +85 ยฐC | 0.01 ยฐC | ยฑ 1 ยฐC | -| ๐Ÿ’ง Humidity | 0 โ†’ 100 %RH | 0.008 %RH | ยฑ 3 %RH | -| ๐Ÿ”ต Pressure | 300 โ†’ 1100 hPa | 0.008 hPa | ยฑ 0.0018 hPa | +| Topic | File | +|-------|------| +| Hardware wiring and IยฒC setup | [docs/hardware.md](docs/hardware.md) | +| System architecture and data flow | [docs/architecture.md](docs/architecture.md) | +| Installation and prerequisites | [docs/installation.md](docs/installation.md) | +| Environment variable reference | [docs/configuration.md](docs/configuration.md) | +| CLI, HTTP API, cron, Docker usage | [docs/usage.md](docs/usage.md) | +| REST API endpoint reference | [docs/api-reference.md](docs/api-reference.md) | +| Home Assistant + MQTT integration | [docs/home-assistant.md](docs/home-assistant.md) | +| Test suite, lint, CI pipeline | [docs/development.md](docs/development.md) | +| Project structure and dependencies | [docs/project-structure.md](docs/project-structure.md) | --- *Bosch BME280 datasheet: [BST-BME280-DS002][bme280-datasheet]* [bme280-datasheet]: https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bme280-ds002.pdf -[ha-mqtt-discovery]: https://www.home-assistant.io/integrations/mqtt/#mqtt-discovery diff --git a/docs/api-reference.md b/docs/api-reference.md new file mode 100644 index 0000000..e357111 --- /dev/null +++ b/docs/api-reference.md @@ -0,0 +1,136 @@ +# ๐Ÿ“ก API Reference + +Base URL: `http://:5000` + +--- + +## `GET /health` + +Service liveness check. + +**Response `200`:** +```json +{ + "status": "ok" +} +``` + +--- + +## `GET /` + +Root endpoint. + +**Response `200`:** +```json +{} +``` + +--- + +## `GET /bme280` + +Returns current sensor readings with full device metadata. + +**Response `200`:** +```json +{ + "name": "bme280", + "brand": "Waveshare", + "part_number": "BME280 Environmental Sensor", + "sku": 15231, + "upc": 614961952638, + "chip": { + "id": 96, + "version": 0 + }, + "capabilities": { + "temperature": { + "unit_of_measurement": "ยฐC", + "min": -40, + "max": 85, + "resolution": 0.01, + "accuracy": 1 + }, + "humidity": { + "unit_of_measurement": "%RH", + "min": 0, + "max": 100, + "resolution": 0.008, + "accuracy": 3 + }, + "pressure": { + "unit_of_measurement": "hPa", + "min": 300, + "max": 1100, + "resolution": 0.008, + "accuracy": 0.0018 + } + }, + "data": { + "temperature": 21.55, + "humidity": 44.57, + "pressure": 1005.16 + } +} +``` + +**Response `503`** โ€” sensor unavailable (IยฒC error): +```json +{ + "error": "Sensor unavailable", + "detail": "[Errno 2] No such file or directory: '/dev/i2c-1'" +} +``` + +--- + +## `GET /bme280/publish` + +Reads sensor data and publishes each measurement to the configured MQTT broker. + +**MQTT topics published:** + +| Topic | Payload | Example | +|-------|---------|---------| +| `sensor/bme280_temperature` | float string | `21.55` | +| `sensor/bme280_humidity` | float string | `44.57` | +| `sensor/bme280_pressure` | float string | `1005.16` | + +**Response `200`:** +```json +{ + "published": true, + "topics": [ + "sensor/bme280_temperature", + "sensor/bme280_humidity", + "sensor/bme280_pressure" + ] +} +``` + +**Response `503`** โ€” sensor unavailable: +```json +{ + "error": "Sensor unavailable", + "detail": "..." +} +``` + +**Response `502`** โ€” MQTT broker unreachable: +```json +{ + "error": "MQTT publish failed", + "detail": "Connection refused" +} +``` + +--- + +## Error handling summary + +| HTTP Status | Meaning | Trigger | +|-------------|---------|---------| +| `200` | Success | Normal operation | +| `503` | Sensor unavailable | IยฒC bus error, sensor disconnected | +| `502` | Bad gateway | MQTT broker unreachable or refused connection | diff --git a/docs/architecture.md b/docs/architecture.md new file mode 100644 index 0000000..0ea235d --- /dev/null +++ b/docs/architecture.md @@ -0,0 +1,62 @@ +# ๐Ÿ—๏ธ Architecture + +## System Overview + +``` +โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” +โ”‚ Raspberry Pi โ”‚ +โ”‚ โ”‚ +โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ +โ”‚ โ”‚ bme280.py โ”‚ โ”‚ sensor_api.py โ”‚ โ”‚ +โ”‚ โ”‚ โ”‚ โ”‚ (Flask) โ”‚ โ”‚ +โ”‚ โ”‚ IยฒC driver โ”‚โ—„โ”€โ”€โ”€โ”€โ”‚ GET /bme280 โ”‚ โ”‚ +โ”‚ โ”‚ Calibration โ”‚ โ”‚ GET /bme280/publish โ”‚ โ”‚ +โ”‚ โ”‚ algorithms โ”‚ โ”‚ GET /health โ”‚ โ”‚ +โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ +โ”‚ โ”‚ โ”‚ โ”‚ +โ”‚ โ–ผ โ–ผ โ”‚ +โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ +โ”‚ โ”‚ BME280 chip โ”‚ โ”‚ MQTT Broker โ”‚ โ”‚ +โ”‚ โ”‚ (IยฒC 0x77) โ”‚ โ”‚ (Home Assistant) โ”‚ โ”‚ +โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ +โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ +``` + +## Components + +### `bme280.py` โ€” IยฒC Driver + +Low-level sensor driver. Handles: + +- Soft reset and NVM copy wait on each read (Bosch SensorAPI spec) +- Calibration data extraction from EEPROM registers (`0x88`, `0xA1`, `0xE1`) +- Raw data reading from `0xF7` (8 bytes: pressure, temperature, humidity) +- Bosch compensation algorithms (double-precision floating point) +- IIR filter configuration via register `0xF5` +- Measurement completion polling via status register `0xF3` + +### `sensor_api.py` โ€” HTTP API + +Flask application exposing three routes: + +| Route | Role | +|-------|------| +| `GET /health` | Service liveness check | +| `GET /bme280` | Returns full sensor reading as JSON | +| `GET /bme280/publish` | Reads sensor and publishes to MQTT broker | + +All configuration (MQTT credentials, IยฒC address, Flask port) is loaded from environment variables via `python-dotenv`. + +## Data Flow + +``` +BME280 chip โ”€โ”€IยฒCโ”€โ”€โ–บ bme280.py โ”€โ”€โ–บ sensor_api.py โ”€โ”€HTTPโ”€โ”€โ–บ client (curl / cron) + โ”‚ + MQTT publish + โ”‚ + โ–ผ + MQTT Broker + โ”‚ + โ–ผ + Home Assistant +``` diff --git a/docs/configuration.md b/docs/configuration.md new file mode 100644 index 0000000..9ebe318 --- /dev/null +++ b/docs/configuration.md @@ -0,0 +1,56 @@ +# โš™๏ธ Configuration + +All settings are driven by environment variables. Copy `.env.example` to `.env` and fill in your values. + +```bash +cp .env.example .env +``` + +> ๐Ÿ”’ `.env` is listed in `.gitignore` โ€” it will never be committed. + +## Reference + +### Sensor (IยฒC) + +| Variable | Default | Description | +|----------|---------|-------------| +| `BME280_I2C_BUS` | `1` | IยฒC bus number (`1` for Pi Rev 2+, `0` for Rev 1) | +| `BME280_I2C_ADDRESS` | `0x77` | Sensor IยฒC address (`0x77` or `0x76` depending on SDO pin wiring) | +| `BME280_IIR_FILTER` | `0` | IIR filter coefficient: `0`=off, `1`=2ร—, `2`=4ร—, `3`=8ร—, `4`=16ร— | + +> ๐Ÿ’ก For indoor use (stable environment), `BME280_IIR_FILTER=2` (4ร— coefficient) smooths out pressure spikes from door slams or air currents. + +### MQTT + +| Variable | Default | Description | +|----------|---------|-------------| +| `MQTT_BROKER_HOST` | `localhost` | IP or hostname of your MQTT broker | +| `MQTT_USERNAME` | _(empty)_ | MQTT username (leave empty for anonymous connections) | +| `MQTT_PASSWORD` | _(empty)_ | MQTT password | +| `MQTT_CLIENT_ID` | `rpi-bme280` | MQTT client identifier โ€” must be unique per broker | + +### Flask API + +| Variable | Default | Description | +|----------|---------|-------------| +| `FLASK_PORT` | `5000` | HTTP port for the API server | +| `FLASK_DEBUG` | `false` | Enable Flask debug mode (`true` / `false`) โ€” keep `false` in production | + +## Example `.env` + +```ini +# Sensor +BME280_I2C_BUS=1 +BME280_I2C_ADDRESS=0x77 +BME280_IIR_FILTER=0 + +# MQTT +MQTT_BROKER_HOST=192.168.1.10 +MQTT_USERNAME=rpi-bme280 +MQTT_PASSWORD=your_password_here +MQTT_CLIENT_ID=rpi-bme280 + +# Flask +FLASK_PORT=5000 +FLASK_DEBUG=false +``` diff --git a/docs/development.md b/docs/development.md new file mode 100644 index 0000000..3cbdd56 --- /dev/null +++ b/docs/development.md @@ -0,0 +1,104 @@ +# ๐Ÿ› ๏ธ Development + +## Install dev dependencies + +```bash +pip install -r requirements-dev.txt +``` + +## Run the test suite + +```bash +pytest tests/ -v +``` + +Expected output: + +``` +tests/test_api.py::test_health_returns_ok PASSED +tests/test_api.py::test_index_returns_empty_json PASSED +tests/test_api.py::test_bme280_returns_sensor_data PASSED +tests/test_api.py::test_bme280_returns_503_when_sensor_unavailable PASSED +tests/test_api.py::test_publish_returns_200_with_topics PASSED +tests/test_api.py::test_publish_returns_503_when_sensor_unavailable PASSED +tests/test_api.py::test_publish_returns_502_when_mqtt_fails PASSED +tests/test_bme280.py::test_get_short_positive PASSED +tests/test_bme280.py::test_get_short_negative PASSED +tests/test_bme280.py::test_get_ushort PASSED +tests/test_bme280.py::test_get_char_positive PASSED +tests/test_bme280.py::test_get_char_negative PASSED +tests/test_bme280.py::test_get_uchar PASSED +tests/test_bme280.py::test_read_id_returns_chip_id_and_version PASSED +tests/test_bme280.py::test_read_id_custom_chip_id PASSED +tests/test_bme280.py::test_nvm_copy_timeout_raises_oserror PASSED +tests/test_bme280.py::test_sensor_returns_required_keys PASSED +tests/test_bme280.py::test_sensor_data_has_three_measurements PASSED +tests/test_bme280.py::test_humidity_clamped_within_range PASSED +tests/test_bme280.py::test_pressure_zero_when_p1_calibration_is_zero PASSED + +20 passed in 0.22s +``` + +> All tests run without physical hardware โ€” `smbus2` is fully mocked via `unittest.mock`. + +## Run tests in Docker + +No hardware required. The `test` target in the Dockerfile installs dev dependencies and runs pytest. + +```bash +docker compose run --rm test +# or directly: +docker build --target test -t bme280-test . && docker run --rm bme280-test +``` + +## Lint and type-check + +```bash +# Style (PEP 8, max line length 120) +flake8 bme280.py sensor_api.py tests/ + +# Static type checking +mypy bme280.py sensor_api.py +``` + +Both tools are configured via `setup.cfg` / `pytest.ini` and run automatically in the GitHub Actions CI pipeline on every push to `develop`. + +## CI pipeline + +Three jobs run in parallel on each push: + +| Job | Tool | What it checks | +|-----|------|----------------| +| `lint` | flake8 + mypy | Style and type correctness | +| `test` | Docker + pytest | All 20 unit tests (no hardware) | +| `security` | pip-audit | Known CVEs in dependencies | + +See `.github/workflows/ci.yml` for the full configuration. + +## Test architecture + +### Driver tests (`tests/test_bme280.py`) + +The IยฒC bus (`smbus2.SMBus`) is patched at the class level so no hardware is required: + +```python +@patch("bme280.smbus2.SMBus") +def test_sensor_returns_required_keys(mock_smbus_cls: MagicMock) -> None: + mock_smbus_cls.return_value.__enter__.return_value = make_mock_bus() + result = sensor() + assert "data" in result +``` + +The `make_mock_bus()` helper configures realistic `side_effect` sequences that mirror actual IยฒC traffic: chip ID read, NVM copy status, calibration registers, raw measurement bytes. + +### API tests (`tests/test_api.py`) + +The Flask test client is used with `sensor` and `mqtt_publish.multiple` patched: + +```python +@patch("sensor_api.sensor") +def test_bme280_returns_sensor_data(mock_sensor: MagicMock, client: FlaskClient) -> None: + mock_sensor.return_value = {...} + response = client.get("/bme280") + assert response.status_code == 200 +``` diff --git a/docs/hardware.md b/docs/hardware.md new file mode 100644 index 0000000..bb795e7 --- /dev/null +++ b/docs/hardware.md @@ -0,0 +1,59 @@ +# ๐Ÿ”Œ Hardware + +## Components + +| Component | Details | +|-----------|---------| +| **Sensor** | Bosch BME280 โ€” Waveshare Environmental Sensor (SKU 15231) | +| **Board** | Raspberry Pi Rev 2+ (IยฒC bus 1) | +| **Interface** | IยฒC โ€” default address `0x77`, alternate `0x76` (SDO pin) | +| **Protocol** | SMBus via `/dev/i2c-1` | + +## Wiring (Raspberry Pi GPIO) + +``` +BME280 Raspberry Pi +โ”€โ”€โ”€โ”€โ”€โ”€ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +VCC โ”€โ”€โ”€โ”€โ”€โ”€โ–บ Pin 1 (3.3V) +GND โ”€โ”€โ”€โ”€โ”€โ”€โ–บ Pin 6 (GND) +SDA โ”€โ”€โ”€โ”€โ”€โ”€โ–บ Pin 3 (GPIO2 / SDA1) +SCL โ”€โ”€โ”€โ”€โ”€โ”€โ–บ Pin 5 (GPIO3 / SCL1) +``` + +> ๐Ÿ’ก Make sure IยฒC is enabled: `sudo raspi-config` โ†’ Interface Options โ†’ I2C โ†’ Enable + +## IยฒC Address + +The BME280 supports two IยฒC addresses depending on the SDO pin wiring: + +| SDO pin | Address | +|---------|---------| +| GND | `0x76` | +| VCC | `0x77` (default) | + +Override via environment variable: `BME280_I2C_ADDRESS=0x76` (see [Configuration](configuration.md)). + +## Sensor Specifications + +| Measurement | Range | Resolution | Accuracy | +|-------------|-------|------------|----------| +| ๐ŸŒก๏ธ Temperature | -40 ยฐC โ†’ +85 ยฐC | 0.01 ยฐC | ยฑ 1 ยฐC | +| ๐Ÿ’ง Humidity | 0 โ†’ 100 %RH | 0.008 %RH | ยฑ 3 %RH | +| ๐Ÿ”ต Pressure | 300 โ†’ 1100 hPa | 0.008 hPa | ยฑ 0.0018 hPa | + +## Enabling IยฒC on Raspberry Pi + +```bash +sudo raspi-config # Interface Options โ†’ I2C โ†’ Enable +sudo reboot + +# Verify the sensor is detected +i2cdetect -y 1 +# Expected: address 0x77 (or 0x76) appears in the grid +``` + +--- + +*Bosch BME280 datasheet: [BST-BME280-DS002][bme280-datasheet]* + +[bme280-datasheet]: https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bme280-ds002.pdf diff --git a/docs/home-assistant.md b/docs/home-assistant.md new file mode 100644 index 0000000..a4012b8 --- /dev/null +++ b/docs/home-assistant.md @@ -0,0 +1,139 @@ +# ๐Ÿ  Home Assistant Integration + +## How it works + +``` +BME280 sensor + โ”‚ IยฒC + โ–ผ +bme280.py (driver) + โ”‚ temperature / pressure / humidity + โ–ผ +sensor_api.py (Flask) + โ”‚ GET /bme280/publish + โ–ผ +MQTT broker (Mosquitto) โ—„โ”€โ”€ Home Assistant polls topics + โ”‚ + โ”œโ”€โ”€ sensor/bme280_temperature โ†’ 21.55 + โ”œโ”€โ”€ sensor/bme280_humidity โ†’ 44.57 + โ””โ”€โ”€ sensor/bme280_pressure โ†’ 1005.16 + โ”‚ + โ–ผ + Home Assistant dashboard +``` + +Every call to `/bme280/publish` (manually or via cron) pushes the three values to the broker. Home Assistant reads them in real time and updates the entity states. + +--- + +## Prerequisites + +### 1. Install Mosquitto in Home Assistant + +In HA: **Settings โ†’ Add-ons โ†’ Mosquitto broker** โ†’ Install โ†’ Start. + +> ๐Ÿ’ก Enable "Start on boot" to survive reboots. + +### 2. Create a dedicated MQTT user + +**Settings โ†’ People โ†’ Users** โ†’ Add user (e.g. `rpi-bme280`). This user will be used by the Raspberry Pi to publish. + +### 3. Enable the MQTT integration + +**Settings โ†’ Devices & Services โ†’ Add integration โ†’ MQTT** โ†’ point to `localhost:1883` with the user created above. + +### 4. Set credentials in `.env` + +```ini +MQTT_BROKER_HOST=192.168.1.10 # HA host IP +MQTT_USERNAME=rpi-bme280 +MQTT_PASSWORD=your_password +``` + +> ๐Ÿ”’ Never commit `.env` โ€” it is listed in `.gitignore`. + +--- + +## Option A โ€” MQTT auto-discovery (recommended) + +Home Assistant supports [MQTT discovery][ha-mqtt-discovery]: publish a JSON config payload once and the entity appears automatically in the UI โ€” no `configuration.yaml` edit needed. + +Run this once from the Pi (replace `` and credentials): + +```bash +mosquitto_pub -h -u rpi-bme280 -P \ + -t "homeassistant/sensor/bme280_temperature/config" \ + -m '{"name":"BME280 Temperature","state_topic":"sensor/bme280_temperature","unit_of_measurement":"ยฐC","device_class":"temperature","state_class":"measurement","unique_id":"bme280_temperature"}' + +mosquitto_pub -h -u rpi-bme280 -P \ + -t "homeassistant/sensor/bme280_humidity/config" \ + -m '{"name":"BME280 Humidity","state_topic":"sensor/bme280_humidity","unit_of_measurement":"%","device_class":"humidity","state_class":"measurement","unique_id":"bme280_humidity"}' + +mosquitto_pub -h -u rpi-bme280 -P \ + -t "homeassistant/sensor/bme280_pressure/config" \ + -m '{"name":"BME280 Pressure","state_topic":"sensor/bme280_pressure","unit_of_measurement":"hPa","device_class":"atmospheric_pressure","state_class":"measurement","unique_id":"bme280_pressure"}' +``` + +The three entities appear under **Settings โ†’ Devices & Services โ†’ MQTT** within seconds. + +--- + +## Option B โ€” Manual MQTT sensors + +If you prefer explicit config, add to your `configuration.yaml`: + +```yaml +mqtt: + sensor: + - name: "BME280 Temperature" + state_topic: "sensor/bme280_temperature" + unit_of_measurement: "ยฐC" + device_class: temperature + state_class: measurement + + - name: "BME280 Humidity" + state_topic: "sensor/bme280_humidity" + unit_of_measurement: "%" + device_class: humidity + state_class: measurement + + - name: "BME280 Pressure" + state_topic: "sensor/bme280_pressure" + unit_of_measurement: "hPa" + device_class: atmospheric_pressure + state_class: measurement +``` + +Then: **Developer Tools โ†’ YAML โ†’ Check configuration โ†’ Restart**. + +--- + +## Automation example + +Trigger an alert when humidity exceeds 70%: + +```yaml +automation: + - alias: "High humidity alert" + trigger: + - platform: numeric_state + entity_id: sensor.bme280_humidity + above: 70 + action: + - service: notify.mobile_app + data: + message: "โš ๏ธ Humidity is {{ states('sensor.bme280_humidity') }}%" +``` + +--- + +## Troubleshooting + +| Symptom | Check | +|---------|-------| +| Entity stuck at `unavailable` | Verify cron is running: `crontab -l` | +| `502` on `/bme280/publish` | Broker unreachable โ€” check `MQTT_BROKER_HOST` and broker is up | +| No entity in HA after discovery | Check discovery is enabled in MQTT integration settings | +| Wrong values | Confirm `BME280_I2C_ADDRESS` matches your wiring (`0x76` vs `0x77`) | + +[ha-mqtt-discovery]: https://www.home-assistant.io/integrations/mqtt/#mqtt-discovery diff --git a/docs/installation.md b/docs/installation.md new file mode 100644 index 0000000..02003fb --- /dev/null +++ b/docs/installation.md @@ -0,0 +1,60 @@ +# ๐Ÿ“ฆ Installation + +## Prerequisites + +- Raspberry Pi Rev 2+ with Raspbian / Raspberry Pi OS +- BME280 sensor wired via IยฒC (see [Hardware](hardware.md)) +- Python 3.10+ +- IยฒC enabled on the Pi + +```bash +# Enable IยฒC +sudo raspi-config # Interface Options โ†’ I2C โ†’ Enable +sudo reboot +``` + +## Clone & install + +```bash +git clone https://github.com/guillaumedelre/bme280.git +cd bme280 + +pip install -r requirements.txt +``` + +## Configure + +```bash +cp .env.example .env +nano .env # fill in your MQTT broker details +``` + +See [Configuration](configuration.md) for the full list of available variables. + +## Verify hardware + +```bash +# Check the sensor is detected on the IยฒC bus +i2cdetect -y 1 +# Address 0x77 (or 0x76) should appear + +# Run a quick CLI read +python bme280.py +``` + +Expected output: + +``` +Chip ID : 96 +Version : 0 +Temperature : 21.55 ยฐC +Pressure : 1005.16 hPa +Humidity : 44.57 %RH +``` + +## Start the API + +```bash +python sensor_api.py +# Listening on http://0.0.0.0:5000 +``` diff --git a/docs/project-structure.md b/docs/project-structure.md new file mode 100644 index 0000000..2d6a27a --- /dev/null +++ b/docs/project-structure.md @@ -0,0 +1,65 @@ +# ๐Ÿ“ Project Structure + +``` +bme280/ +โ”œโ”€โ”€ bme280.py # IยฒC driver โ€” Bosch calibration algorithms +โ”œโ”€โ”€ sensor_api.py # Flask HTTP API + MQTT publisher +โ”‚ +โ”œโ”€โ”€ tests/ +โ”‚ โ”œโ”€โ”€ test_bme280.py # Driver unit tests (hardware mocked) +โ”‚ โ””โ”€โ”€ test_api.py # API route tests (Flask test client) +โ”‚ +โ”œโ”€โ”€ docs/ +โ”‚ โ”œโ”€โ”€ hardware.md # Components, wiring, IยฒC address +โ”‚ โ”œโ”€โ”€ architecture.md # System diagram, component descriptions +โ”‚ โ”œโ”€โ”€ installation.md # Prerequisites, clone, configure +โ”‚ โ”œโ”€โ”€ configuration.md # Full environment variable reference +โ”‚ โ”œโ”€โ”€ usage.md # CLI, HTTP API, cron, Docker +โ”‚ โ”œโ”€โ”€ api-reference.md # Endpoint documentation with examples +โ”‚ โ”œโ”€โ”€ home-assistant.md # MQTT auto-discovery, HA integration +โ”‚ โ”œโ”€โ”€ development.md # Test suite, lint, CI pipeline +โ”‚ โ””โ”€โ”€ project-structure.md +โ”‚ +โ”œโ”€โ”€ .github/ +โ”‚ โ””โ”€โ”€ workflows/ +โ”‚ โ””โ”€โ”€ ci.yml # CI: lint, test, security (pip-audit) +โ”‚ +โ”œโ”€โ”€ Dockerfile # Multi-stage: test target + app target +โ”œโ”€โ”€ docker-compose.yml # Services: test, app +โ”‚ +โ”œโ”€โ”€ .env.example # Configuration template +โ”œโ”€โ”€ requirements.txt # Runtime dependencies +โ”œโ”€โ”€ requirements-dev.txt # Dev/test dependencies +โ”œโ”€โ”€ pytest.ini # Pytest configuration +โ””โ”€โ”€ .gitignore +``` + +## Key dependencies + +| Package | Version | Role | +|---------|---------|------| +| `smbus2` | >= 0.4.3 | IยฒC communication via `/dev/i2c-*` | +| `flask` | >= 3.0.0 | HTTP API framework | +| `paho-mqtt` | >= 1.6.1 | MQTT client (fire-and-forget publish) | +| `python-dotenv` | >= 1.0.0 | `.env` file loading at startup | + +Dev/test only: + +| Package | Version | Role | +|---------|---------|------| +| `pytest` | >= 8.0.0 | Test runner | +| `pytest-flask` | >= 1.3.0 | Flask test client fixture | +| `flake8` | >= 7.0.0 | Style linter (PEP 8, max line 120) | +| `mypy` | >= 1.0.0 | Static type checker | + +## Sensor Specifications + +| Measurement | Range | Resolution | Accuracy | +|-------------|-------|------------|----------| +| Temperature | -40 ยฐC to +85 ยฐC | 0.01 ยฐC | +/- 1 ยฐC | +| Humidity | 0 to 100 %RH | 0.008 %RH | +/- 3 %RH | +| Pressure | 300 to 1100 hPa | 0.008 hPa | +/- 0.0018 hPa | + +[BME280 datasheet][bme280-datasheet] + +[bme280-datasheet]: https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bme280-ds002.pdf diff --git a/docs/usage.md b/docs/usage.md new file mode 100644 index 0000000..2edf80e --- /dev/null +++ b/docs/usage.md @@ -0,0 +1,88 @@ +# ๐Ÿš€ Usage + +## CLI + +Read sensor data directly from the terminal (requires hardware): + +```bash +python bme280.py +``` + +``` +Chip ID : 96 +Version : 0 +Temperature : 21.55 ยฐC +Pressure : 1005.16 hPa +Humidity : 44.57 %RH +``` + +Use an alternate IยฒC address: + +```bash +BME280_I2C_ADDRESS=0x76 python bme280.py +``` + +## HTTP API + +Start the API server: + +```bash +python sensor_api.py +# Listening on http://0.0.0.0:5000 +``` + +```bash +# Health check +curl http://rpi.local:5000/health + +# Read sensor data +curl http://rpi.local:5000/bme280 | python -m json.tool + +# Publish to MQTT +curl http://rpi.local:5000/bme280/publish +``` + +See [API Reference](api-reference.md) for the full endpoint documentation. + +## Automate with cron + +Publish sensor data every minute: + +```bash +crontab -e +``` + +```cron +* * * * * curl -s http://localhost:5000/bme280/publish >> /var/log/bme280.log 2>&1 +``` + +## Docker + +### Run the API (Raspberry Pi) + +```bash +# Build the app image +docker build --target app -t bme280-app . + +# Run with IยฒC device passthrough and env file +docker run -d \ + --name bme280 \ + --device /dev/i2c-1:/dev/i2c-1 \ + --env-file .env \ + -p 5000:5000 \ + bme280-app +``` + +Or with docker compose (uncomment `devices` in `docker-compose.yml` first): + +```bash +docker compose up app +``` + +### Run tests (no hardware required) + +```bash +docker compose run --rm test +# or +docker build --target test -t bme280-test . && docker run --rm bme280-test +```