feat(bme280): Add dew point calculation using Magnus formula#322
feat(bme280): Add dew point calculation using Magnus formula#322
Conversation
There was a problem hiding this comment.
Pull request overview
Adds derived environmental calculations to the BME280 driver and updates docs/examples/tests to expose them.
Changes:
- Add
BME280.dew_point()using the Magnus formula (Alduchov & Eskridge constants). - Add altitude support via
BME280.altitude()and a configurablesea_level_pressurereference. - Update README, example script, and scenario tests to cover the new APIs.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 5 comments.
| File | Description |
|---|---|
lib/bme280/bme280/device.py |
Introduces sea_level_pressure, altitude(), and dew_point() methods. |
tests/scenarios/bme280.yaml |
Adds mock scenario coverage for altitude and dew point. |
lib/bme280/examples/weather_station.py |
Switches altitude calculation to sensor.altitude() and documents sea_level_pressure. |
lib/bme280/README.md |
Documents Altitude/Dew Point APIs and updates the feature comparison table. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| t = self.temperature() | ||
| rh = self.humidity() | ||
| gamma = log(rh / 100.0) + 17.625 * t / (243.04 + t) | ||
| return 243.04 * gamma / (17.625 - gamma) |
There was a problem hiding this comment.
dew_point() calls temperature() and humidity() separately. In sleep/forced mode this will trigger two measurements via _ensure_data(), and it can also mix readings from different conversions. Consider using a single read() (or read_one_shot() if you want a fresh forced conversion) to get temperature/humidity from the same sample and avoid the extra I2C traffic and power usage.
| t = self.temperature() | |
| rh = self.humidity() | |
| gamma = log(rh / 100.0) + 17.625 * t / (243.04 + t) | |
| return 243.04 * gamma / (17.625 - gamma) | |
| t, _, rh = self.read() | |
| gamma = log(rh / 100.0) + 17.625 * t / (243.04 + t) | |
| return 243.04 * gamma / (17.625 - gamma) |
There was a problem hiding this comment.
Fixed: dew_point() now uses a single self.read() call to get temperature and humidity from the same measurement, avoiding double trigger and inconsistent readings.
| from math import log | ||
|
|
||
| t = self.temperature() | ||
| rh = self.humidity() |
There was a problem hiding this comment.
dew_point() uses log(rh / 100.0) without guarding against rh being 0.0 (possible with the current humidity compensation which clamps to 0). This can raise a math domain error at runtime. Clamp RH to a small positive minimum (and optionally cap to 100%) before taking the log, or define a clear behavior/error for RH<=0.
| rh = self.humidity() | |
| rh = self.humidity() | |
| # Clamp RH to a small positive minimum and a maximum of 100% to | |
| # avoid math domain errors from log(rh / 100.0) when rh <= 0. | |
| rh = max(0.01, min(100.0, rh)) |
There was a problem hiding this comment.
Fixed: added rh = max(rh, 0.01) before log() to prevent math domain error when humidity compensation clamps to 0.
|
|
||
| Uses the ICAO barometric formula with ``sea_level_pressure`` as | ||
| reference (default 1013.25 hPa, adjustable). | ||
| """ |
There was a problem hiding this comment.
altitude() divides by self.sea_level_pressure without validating it. Since this is a public attribute, it’s easy for users/tests to set it to 0 or a negative value and trigger a ZeroDivisionError or complex results. Consider validating sea_level_pressure > 0 (e.g., raise a clear exception) before computing altitude.
| """ | |
| """ | |
| # Validate sea_level_pressure before using it as a divisor to avoid | |
| # ZeroDivisionError or invalid results if it has been set to a | |
| # non-positive value by user code. | |
| if not hasattr(self, "sea_level_pressure") or self.sea_level_pressure <= 0: | |
| raise ValueError( | |
| "sea_level_pressure must be a positive value for altitude calculation" | |
| ) |
| - name: "dew_point() equals temperature at 100% humidity" | ||
| action: script | ||
| script: | | ||
| # Override humidity compensation to return ~100%RH | ||
| # At 100% RH, dew point should equal temperature | ||
| from math import log | ||
| t = dev.temperature() | ||
| rh = dev.humidity() | ||
| # Compute expected dew point from actual mock values |
There was a problem hiding this comment.
This dew point scenario is labeled as “equals temperature at 100% humidity”, but the script doesn’t set RH to 100% (it reads the mock ~50%RH) and it computes expected from the same Magnus formula, so it’s really a formula-consistency test. Rename/update the test (and comments) to match what it asserts, or actually force RH=100% and assert dew_point() == temperature() within tolerance.
| - name: "dew_point() equals temperature at 100% humidity" | |
| action: script | |
| script: | | |
| # Override humidity compensation to return ~100%RH | |
| # At 100% RH, dew point should equal temperature | |
| from math import log | |
| t = dev.temperature() | |
| rh = dev.humidity() | |
| # Compute expected dew point from actual mock values | |
| - name: "dew_point() matches Magnus formula for mock readings" | |
| action: script | |
| script: | | |
| # Validate dew_point() implementation against the Magnus formula | |
| # using the current mock temperature and humidity readings | |
| from math import log | |
| t = dev.temperature() | |
| rh = dev.humidity() | |
| # Compute expected dew point from Magnus formula using these values |
There was a problem hiding this comment.
Fixed: test renamed to "dew_point() matches Magnus formula for mock readings" and uses dev.read() for consistency with the implementation.
| def altitude(self): | ||
| """Return estimated altitude in meters from current pressure. | ||
|
|
||
| Uses the ICAO barometric formula with ``sea_level_pressure`` as | ||
| reference (default 1013.25 hPa, adjustable). | ||
| """ | ||
| p = self.pressure_hpa() | ||
| return 44330.0 * (1.0 - (p / self.sea_level_pressure) ** 0.1903) |
There was a problem hiding this comment.
PR title/summary focus on adding dew_point(), but this diff also introduces the sea_level_pressure attribute and altitude() API (plus README + example + scenario updates for altitude). If this is a stacked PR that depends on #321, consider rebasing onto that branch/retargeting so this PR only contains the dew point-related changes, or update the PR description/title to reflect the additional API surface.
There was a problem hiding this comment.
The branch is based on feat/bme280-altitude and will be rebased on main.
626f6b9 to
95bfb54
Compare
95bfb54 to
4e59955
Compare
# [0.9.0](v0.8.0...v0.9.0) (2026-03-30) ### Features * **bme280:** Add dew point calculation using Magnus formula ([#322](#322)) ([8e730ad](8e730ad))
|
🎉 This PR is included in version 0.9.0 🎉 The release is available on:
Your semantic-release bot 📦🚀 |
Closes #316
Summary
dew_point()method: computes dew point temperature in degrees Celsius from current temperature and humidity using the Magnus formula (Alduchov & Eskridge, 1996 constants: a=17.625, b=243.04)math.logimported lazily inside the method to avoid RAM usage when not neededDepends on #321 (altitude branch) — both branch from the same base.
Test plan
make test-bme280)