feat(bme280): Implement _ensure_data() auto-trigger pattern#318
Conversation
There was a problem hiding this comment.
Pull request overview
Adds an auto-trigger “ensure fresh data” pattern to the BME280 driver so measurement APIs can transparently perform a forced conversion when the device is in sleep mode (aligning with the repo’s broader auto-trigger conventions).
Changes:
- Add
_is_sleep_mode()and_ensure_data()helpers to detect sleep mode and trigger/wait for a forced measurement. - Update
temperature(),pressure_hpa(),humidity(), andread()to call_ensure_data()before reading sensor registers. - Add mock scenario tests in
tests/scenarios/bme280.yamlto validate sleep/normal detection and auto-trigger behavior.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| lib/bme280/bme280/device.py | Introduces _ensure_data()/_is_sleep_mode() and wires auto-trigger into measurement methods. |
| tests/scenarios/bme280.yaml | Adds mock scenarios covering sleep/normal mode checks and auto-triggered measurement writes. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| def _ensure_data(self): | ||
| """Trigger a forced measurement if the sensor is in sleep mode. | ||
|
|
||
| In normal mode this is a no-op. In sleep mode it triggers a | ||
| single conversion and waits for completion so that subsequent | ||
| register reads return fresh data. | ||
| """ | ||
| if self._is_sleep_mode(): | ||
| self.trigger_one_shot() | ||
| self._wait_measurement() |
There was a problem hiding this comment.
_ensure_data() relies on _wait_measurement(), which currently raises BME280Error("BME280 measurement timeout"). The auto-trigger convention in issue #159 calls for raising OSError on timeout; as implemented, callers will see a driver-specific exception type instead. Consider changing the timeout exception to OSError (or making BME280Error inherit from OSError) so timeout handling is consistent with the convention across drivers.
There was a problem hiding this comment.
The project convention uses driver-specific exceptions (e.g. WSENPADSTimeout in wsen-pads, BME280Error here), all inheriting from Exception, not OSError. This is consistent across all drivers in the repo. Keeping BME280Error for now to stay aligned.
There was a problem hiding this comment.
| def read(self): | ||
| """Return (temperature_c, pressure_hpa, humidity_rh) tuple.""" | ||
| """Return (temperature_c, pressure_hpa, humidity_rh) tuple. | ||
|
|
||
| If the sensor is in sleep mode, a forced measurement is triggered | ||
| automatically before reading. | ||
| """ | ||
| self._ensure_data() | ||
| raw_temp, raw_press, raw_hum = self._read_raw() |
There was a problem hiding this comment.
read_one_shot() calls read(), and read() now calls _ensure_data(). On real BME280 hardware, a forced measurement typically returns the sensor to sleep mode after completion; that would make read() immediately trigger a second forced measurement, so read_one_shot() could perform two conversions instead of one. To avoid double-triggering, consider having read_one_shot() read/compensate directly (bypassing _ensure_data), or add an internal/read parameter that skips auto-trigger when read_one_shot() has already triggered a conversion.
There was a problem hiding this comment.
Fixed in d39f467: read_one_shot() now reads and compensates directly instead of calling read(), avoiding the double-trigger. A new test "read_one_shot does not double-trigger" verifies that exactly one forced mode write occurs.
| result = triggered and abs(t - 25.08) < 0.1 | ||
| expect_true: true | ||
| mode: [mock] |
There was a problem hiding this comment.
The new auto-trigger coverage only verifies temperature() and read() behavior in sleep mode. Since pressure_hpa() and humidity() were also updated to call _ensure_data(), consider adding similar mock assertions for those methods (and/or asserting p and h in the read() auto-trigger test) to ensure all updated APIs are covered.
| result = triggered and abs(t - 25.08) < 0.1 | |
| expect_true: true | |
| mode: [mock] | |
| result = ( | |
| triggered | |
| and abs(t - 25.08) < 0.1 | |
| and abs(p - 1009.21) < 0.1 | |
| and abs(h - 50.57) < 0.1 | |
| ) | |
| expect_true: true | |
| mode: [mock] | |
| - name: "pressure_hpa() auto-triggers in sleep mode" | |
| action: script | |
| script: | | |
| dev.power_off() | |
| i2c.clear_write_log() | |
| p = dev.pressure_hpa() | |
| log = i2c.get_write_log() | |
| triggered = any(reg == 0xF4 and (data[0] & 0x03) == 0x01 for reg, data in log) | |
| result = triggered and abs(p - 1009.21) < 0.1 | |
| expect_true: true | |
| mode: [mock] | |
| - name: "humidity() auto-triggers in sleep mode" | |
| action: script | |
| script: | | |
| dev.power_off() | |
| i2c.clear_write_log() | |
| h = dev.humidity() | |
| log = i2c.get_write_log() | |
| triggered = any(reg == 0xF4 and (data[0] & 0x03) == 0x01 for reg, data in log) | |
| result = triggered and abs(h - 50.57) < 0.1 | |
| expect_true: true | |
| mode: [mock] |
There was a problem hiding this comment.
Fixed in d39f467: added auto-trigger tests for pressure_hpa() and humidity(), and the read() test now also validates pressure and humidity values.
| i2c.clear_write_log() | ||
| dev._ensure_data() | ||
| log = i2c.get_write_log() | ||
| # In normal mode, only a read of ctrl_meas (no write to 0xF4) |
There was a problem hiding this comment.
This comment says "In normal mode, only a read of ctrl_meas (no write to 0xF4)", but the mock I2C write log does not record reads—so the log will be empty here. Consider rewording to avoid implying the read is observable in the write log.
| # In normal mode, only a read of ctrl_meas (no write to 0xF4) | |
| # In normal mode, _ensure_data must not write to ctrl_meas (0xF4); the write log may be empty |
There was a problem hiding this comment.
Fixed in d39f467: reworded comment to clarify that the write log should not contain a forced mode write.
# [0.7.0](v0.6.0...v0.7.0) (2026-03-29) ### Features * **bme280:** Implement _ensure_data() auto-trigger pattern ([#318](#318)) ([a0d344d](a0d344d))
|
🎉 This PR is included in version 0.7.0 🎉 The release is available on:
Your semantic-release bot 📦🚀 |
Closes #159
Summary
_is_sleep_mode(): readsctrl_measregister and checks if mode bits are0x00(sleep)_ensure_data(): if the sensor is in sleep mode, triggers a forced measurement and waits for completion; no-op in normal modetemperature(),pressure_hpa(),humidity(), andread()now call_ensure_data()before reading registers, so they always return fresh data regardless of the current operating moderead_one_shot()is unchanged — it explicitly triggers a forced measurement regardless of mode_is_sleep_mode()in sleep/normal,_ensure_data()trigger/no-op,temperature()andread()auto-trigger verificationTest plan
make test-bme280)