pioreactor-precision-temperature-plugin 0.2.7__tar.gz → 0.2.9__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pioreactor_precision_temperature_plugin-0.2.7/pioreactor_precision_temperature_plugin.egg-info → pioreactor_precision_temperature_plugin-0.2.9}/PKG-INFO +1 -1
- {pioreactor_precision_temperature_plugin-0.2.7 → pioreactor_precision_temperature_plugin-0.2.9}/pioreactor_precision_temperature_plugin/adafruit_mlx90632.py +2 -1
- {pioreactor_precision_temperature_plugin-0.2.7 → pioreactor_precision_temperature_plugin-0.2.9}/pioreactor_precision_temperature_plugin/fir_temperature_automation.py +9 -2
- {pioreactor_precision_temperature_plugin-0.2.7 → pioreactor_precision_temperature_plugin-0.2.9/pioreactor_precision_temperature_plugin.egg-info}/PKG-INFO +1 -1
- {pioreactor_precision_temperature_plugin-0.2.7 → pioreactor_precision_temperature_plugin-0.2.9}/setup.py +1 -1
- {pioreactor_precision_temperature_plugin-0.2.7 → pioreactor_precision_temperature_plugin-0.2.9}/tests/test_fir_temperature_automation.py +65 -0
- {pioreactor_precision_temperature_plugin-0.2.7 → pioreactor_precision_temperature_plugin-0.2.9}/LICENSE.txt +0 -0
- {pioreactor_precision_temperature_plugin-0.2.7 → pioreactor_precision_temperature_plugin-0.2.9}/MANIFEST.in +0 -0
- {pioreactor_precision_temperature_plugin-0.2.7 → pioreactor_precision_temperature_plugin-0.2.9}/README.md +0 -0
- {pioreactor_precision_temperature_plugin-0.2.7 → pioreactor_precision_temperature_plugin-0.2.9}/pioreactor_precision_temperature_plugin/THIRD_PARTY_LICENSE_ADAFRUIT_MLX90632.txt +0 -0
- {pioreactor_precision_temperature_plugin-0.2.7 → pioreactor_precision_temperature_plugin-0.2.9}/pioreactor_precision_temperature_plugin/__init__.py +0 -0
- {pioreactor_precision_temperature_plugin-0.2.7 → pioreactor_precision_temperature_plugin-0.2.9}/pioreactor_precision_temperature_plugin/additional_config.ini +0 -0
- {pioreactor_precision_temperature_plugin-0.2.7 → pioreactor_precision_temperature_plugin-0.2.9}/pioreactor_precision_temperature_plugin/estimators/fir_temperature_estimator_linear_mlx_ambient_pcb_v1.yaml +0 -0
- {pioreactor_precision_temperature_plugin-0.2.7 → pioreactor_precision_temperature_plugin-0.2.9}/pioreactor_precision_temperature_plugin/post_install.sh +0 -0
- {pioreactor_precision_temperature_plugin-0.2.7 → pioreactor_precision_temperature_plugin-0.2.9}/pioreactor_precision_temperature_plugin/ui/contrib/charts/temperature.yaml +0 -0
- {pioreactor_precision_temperature_plugin-0.2.7 → pioreactor_precision_temperature_plugin-0.2.9}/pioreactor_precision_temperature_plugin.egg-info/SOURCES.txt +0 -0
- {pioreactor_precision_temperature_plugin-0.2.7 → pioreactor_precision_temperature_plugin-0.2.9}/pioreactor_precision_temperature_plugin.egg-info/dependency_links.txt +0 -0
- {pioreactor_precision_temperature_plugin-0.2.7 → pioreactor_precision_temperature_plugin-0.2.9}/pioreactor_precision_temperature_plugin.egg-info/entry_points.txt +0 -0
- {pioreactor_precision_temperature_plugin-0.2.7 → pioreactor_precision_temperature_plugin-0.2.9}/pioreactor_precision_temperature_plugin.egg-info/top_level.txt +0 -0
- {pioreactor_precision_temperature_plugin-0.2.7 → pioreactor_precision_temperature_plugin-0.2.9}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pioreactor-precision-temperature-plugin
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.9
|
|
4
4
|
Summary: Precision temperature automation for Pioreactor using FIR + MLX90632
|
|
5
5
|
Home-page: https://github.com/Pioreactor/pioreactor-precision-temperature-plugin
|
|
6
6
|
Author: Cam Davidson-Pilon
|
|
@@ -119,7 +119,8 @@ class MLX90632:
|
|
|
119
119
|
except Exception as exc:
|
|
120
120
|
raise RuntimeError(
|
|
121
121
|
"Failed to initialize MLX90632 I2C device. "
|
|
122
|
-
"
|
|
122
|
+
f"address=0x{address:02X}, i2c_bus_type={type(i2c_bus).__name__}, "
|
|
123
|
+
f"original_error={type(exc).__name__}: {exc}"
|
|
123
124
|
) from exc
|
|
124
125
|
|
|
125
126
|
# Initialize temperature history
|
|
@@ -71,6 +71,7 @@ STABILIZATION_MAX_SLOPE_C_PER_MIN = 0.02
|
|
|
71
71
|
STABILIZATION_WINDOW_S = 2.0 * 60
|
|
72
72
|
STABILIZATION_SLOPE_SAMPLE_DELAY_S = 4.1
|
|
73
73
|
STABILIZATION_TIMEOUT_S = 90 * 60
|
|
74
|
+
DANGER_ZONE_DUTY_CYCLE_REDUCTION_FACTOR = 0.9965
|
|
74
75
|
|
|
75
76
|
available_temperature_automations: dict[str, type["TemperatureAutomationJobFIR"]] = {}
|
|
76
77
|
|
|
@@ -238,6 +239,12 @@ class TemperatureAutomationJobFIR(AutomationJob):
|
|
|
238
239
|
except Exception as e:
|
|
239
240
|
raise exc.HardwareNotFoundError(f"Unable to import MLX90632 dependencies: {e}")
|
|
240
241
|
|
|
242
|
+
if not hardware.is_i2c_device_present(address):
|
|
243
|
+
raise exc.HardwareNotFoundError(
|
|
244
|
+
f"MLX90632 not found at address 0x{address:02X} on I2C bus. "
|
|
245
|
+
"Check mlx_address and sensor wiring."
|
|
246
|
+
)
|
|
247
|
+
|
|
241
248
|
i2c = busio.I2C(hardware.get_scl_pin(), hardware.get_sda_pin())
|
|
242
249
|
sensor = adafruit_mlx90632.MLX90632(i2c, address=address)
|
|
243
250
|
with suppress(Exception):
|
|
@@ -359,9 +366,9 @@ class TemperatureAutomationJobFIR(AutomationJob):
|
|
|
359
366
|
|
|
360
367
|
elif temp > self.MAX_TEMP_TO_REDUCE_HEATING:
|
|
361
368
|
self.logger.debug(
|
|
362
|
-
f"Temperature of heating surface exceeded {self.MAX_TEMP_TO_REDUCE_HEATING}℃ (currently {temp}℃). Reducing heater
|
|
369
|
+
f"Temperature of heating surface exceeded {self.MAX_TEMP_TO_REDUCE_HEATING}℃ (currently {temp}℃). Reducing heater slightly."
|
|
363
370
|
)
|
|
364
|
-
self._update_heater(self.heater_duty_cycle *
|
|
371
|
+
self._update_heater(self.heater_duty_cycle * DANGER_ZONE_DUTY_CYCLE_REDUCTION_FACTOR)
|
|
365
372
|
|
|
366
373
|
return temp
|
|
367
374
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pioreactor-precision-temperature-plugin
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.9
|
|
4
4
|
Summary: Precision temperature automation for Pioreactor using FIR + MLX90632
|
|
5
5
|
Home-page: https://github.com/Pioreactor/pioreactor-precision-temperature-plugin
|
|
6
6
|
Author: Cam Davidson-Pilon
|
|
@@ -4,7 +4,7 @@ from setuptools import setup
|
|
|
4
4
|
|
|
5
5
|
setup(
|
|
6
6
|
name="pioreactor-precision-temperature-plugin",
|
|
7
|
-
version="0.2.
|
|
7
|
+
version="0.2.9",
|
|
8
8
|
python_requires=">=3.13,<3.14",
|
|
9
9
|
license_files=("LICENSE.txt",),
|
|
10
10
|
description="Precision temperature automation for Pioreactor using FIR + MLX90632",
|
|
@@ -111,6 +111,7 @@ def test_setup_mlx_driver_uses_busio_i2c(
|
|
|
111
111
|
monkeypatch.setattr(fta.whoami, "is_testing_env", lambda: False)
|
|
112
112
|
monkeypatch.setattr(fta.hardware, "get_scl_pin", lambda: 11)
|
|
113
113
|
monkeypatch.setattr(fta.hardware, "get_sda_pin", lambda: 13)
|
|
114
|
+
monkeypatch.setattr(fta.hardware, "is_i2c_device_present", lambda _address: True)
|
|
114
115
|
monkeypatch.setitem(sys.modules, "busio", FakeBusIOModule())
|
|
115
116
|
monkeypatch.setattr(plugin_pkg, "adafruit_mlx90632", FakeMLXModule, raising=False)
|
|
116
117
|
|
|
@@ -123,6 +124,40 @@ def test_setup_mlx_driver_uses_busio_i2c(
|
|
|
123
124
|
assert sensor.refresh_rate == FakeMLXModule.REFRESH_1HZ
|
|
124
125
|
|
|
125
126
|
|
|
127
|
+
def test_setup_mlx_driver_raises_clear_error_when_address_missing(
|
|
128
|
+
monkeypatch: pytest.MonkeyPatch,
|
|
129
|
+
) -> None:
|
|
130
|
+
import pioreactor_precision_temperature_plugin as plugin_pkg
|
|
131
|
+
from pioreactor import exc as pr_exc
|
|
132
|
+
|
|
133
|
+
class FakeBusIOModule:
|
|
134
|
+
@staticmethod
|
|
135
|
+
def I2C(_scl: int, _sda: int) -> object:
|
|
136
|
+
return object()
|
|
137
|
+
|
|
138
|
+
class FakeMLXModule:
|
|
139
|
+
MODE_CONTINUOUS = 3
|
|
140
|
+
REFRESH_1HZ = 1
|
|
141
|
+
|
|
142
|
+
class MLX90632:
|
|
143
|
+
def __init__(self, _i2c_bus: object, _address: int) -> None:
|
|
144
|
+
raise AssertionError("should not attempt MLX init when address is missing")
|
|
145
|
+
|
|
146
|
+
monkeypatch.setattr(fta.whoami, "is_testing_env", lambda: False)
|
|
147
|
+
monkeypatch.setattr(fta.hardware, "get_scl_pin", lambda: 11)
|
|
148
|
+
monkeypatch.setattr(fta.hardware, "get_sda_pin", lambda: 13)
|
|
149
|
+
monkeypatch.setattr(fta.hardware, "is_i2c_device_present", lambda _address: False)
|
|
150
|
+
monkeypatch.setitem(sys.modules, "busio", FakeBusIOModule())
|
|
151
|
+
monkeypatch.setattr(plugin_pkg, "adafruit_mlx90632", FakeMLXModule, raising=False)
|
|
152
|
+
|
|
153
|
+
job: Any = object.__new__(fta.TemperatureAutomationJobFIR)
|
|
154
|
+
|
|
155
|
+
with pytest.raises(pr_exc.HardwareNotFoundError) as e:
|
|
156
|
+
job._setup_mlx_driver(address=0x3A)
|
|
157
|
+
|
|
158
|
+
assert "MLX90632 not found at address 0x3A" in str(e.value)
|
|
159
|
+
|
|
160
|
+
|
|
126
161
|
def test_update_volume_from_mqtt_ignores_invalid_payloads() -> None:
|
|
127
162
|
job: Any = object.__new__(fta.TemperatureAutomationJobFIR)
|
|
128
163
|
job._volume_ml = 25.0
|
|
@@ -377,6 +412,36 @@ def test_update_heater_respects_pwm_lock() -> None:
|
|
|
377
412
|
assert calls == [50.0]
|
|
378
413
|
|
|
379
414
|
|
|
415
|
+
def test_check_if_exceeds_max_temp_reduces_heater_gently_in_danger_zone(
|
|
416
|
+
monkeypatch: pytest.MonkeyPatch,
|
|
417
|
+
) -> None:
|
|
418
|
+
monkeypatch.setattr(
|
|
419
|
+
fta,
|
|
420
|
+
"get_pioreactor_model",
|
|
421
|
+
lambda: SimpleNamespace(
|
|
422
|
+
max_temp_to_reduce_heating=50.0,
|
|
423
|
+
max_temp_to_disable_heating=60.0,
|
|
424
|
+
max_temp_to_shutdown=70.0,
|
|
425
|
+
),
|
|
426
|
+
)
|
|
427
|
+
|
|
428
|
+
job: Any = object.__new__(fta.TemperatureAutomationJobFIR)
|
|
429
|
+
job.heater_duty_cycle = 80.0
|
|
430
|
+
job.logger = SimpleNamespace(debug=lambda *_args, **_kwargs: None)
|
|
431
|
+
reductions: list[float] = []
|
|
432
|
+
|
|
433
|
+
def fake_update_heater(new_dc: float) -> bool:
|
|
434
|
+
reductions.append(new_dc)
|
|
435
|
+
return True
|
|
436
|
+
|
|
437
|
+
job._update_heater = fake_update_heater
|
|
438
|
+
|
|
439
|
+
observed_temp = job._check_if_exceeds_max_temp(55.0)
|
|
440
|
+
|
|
441
|
+
assert observed_temp == pytest.approx(55.0)
|
|
442
|
+
assert reductions == [pytest.approx(80.0 * fta.DANGER_ZONE_DUTY_CYCLE_REDUCTION_FACTOR)]
|
|
443
|
+
|
|
444
|
+
|
|
380
445
|
def test_bias_trim_probe_step_ui_uses_store_estimator(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
381
446
|
base = SimpleNamespace(
|
|
382
447
|
estimator_name="baseline-estimator",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|