changeset 20:92367b644e29

Remove support for DHT22 to remove native dependencies. This removes the ability to read from the DHT22 sensor, since it requires native code to run and will not compile on ARM64 (at least for me, which is enough reason to remove it).
author Paul Fisher <paul@pfish.zone>
date Sat, 25 Dec 2021 00:02:03 +0000
parents 7117db65715e
children 1ae7bd2566ef
files requirements.txt setup.py weatherlog/daemon.py weatherlog/reader.py weatherlog/reader_test.py
diffstat 5 files changed, 23 insertions(+), 150 deletions(-) [+]
line wrap: on
line diff
--- a/requirements.txt	Mon Jan 20 23:36:23 2020 -0500
+++ b/requirements.txt	Sat Dec 25 00:02:03 2021 +0000
@@ -1,8 +1,12 @@
-adafruit-blinka
-adafruit-circuitpython-dht
-attrs
-pymongo
-pytz
-requests
-RPi.bme280
-smbus2
+attrs==21.2.0
+certifi==2021.10.8
+charset-normalizer==2.0.9
+idna==3.3
+pymongo==4.0.1
+pytz==2021.3
+requests==2.26.0
+RPi.bme280==0.2.4
+smbus2==0.4.1
+toml==0.10.2
+urllib3==1.26.7
+
--- a/setup.py	Mon Jan 20 23:36:23 2020 -0500
+++ b/setup.py	Sat Dec 25 00:02:03 2021 +0000
@@ -2,12 +2,10 @@
 
 setuptools.setup(
     name='weatherlog',
-    version='0.2.2',
+    version='0.3.0',
     packages=setuptools.find_packages(),
     python_requires='>=3.7',
     install_requires=[
-        'adafruit-blinka',
-        'adafruit-circuitpython-dht',
         'attrs',
         'pymongo',
         'pytz',
--- a/weatherlog/daemon.py	Mon Jan 20 23:36:23 2020 -0500
+++ b/weatherlog/daemon.py	Sat Dec 25 00:02:03 2021 +0000
@@ -43,10 +43,14 @@
 
 
 class SensorType(enum.Enum):
-    DHT22 = 'DHT22'
     BME280 = 'BME280'
 
 
+_SENSOR_FACTORIES = {
+    SensorType.BME280: reader.BME280Reader,
+}
+
+
 @attr.s(auto_attribs=True, frozen=True, slots=True)
 class _Config:
     # The directory to store reading data in.
@@ -77,12 +81,11 @@
     writer = http_writer.HTTPWriter(config.url, config.auth)
     log = logger.BufferedLogger(config.directory, writer)
 
-    if config.sensor is SensorType.DHT22:
-        rdr = reader.DHT22Reader()
-    elif config.sensor is SensorType.BME280:
-        rdr = reader.BME280Reader()
-    else:
-        raise AssertionError('Unknown sensor type')
+    try:
+        factory = _SENSOR_FACTORIES[config.sensor]
+    except KeyError:
+        print(f'Invalid sensor type {config.sensor}.')
+    rdr = factory()
     run(rdr, log, writer)
 
 
--- a/weatherlog/reader.py	Mon Jan 20 23:36:23 2020 -0500
+++ b/weatherlog/reader.py	Sat Dec 25 00:02:03 2021 +0000
@@ -7,9 +7,7 @@
 import time
 import typing as t
 
-import adafruit_dht
 import bme280
-import board
 import smbus2
 
 from . import types
@@ -24,49 +22,6 @@
         raise NotImplementedError()
 
 
-class DHT22Reader(Reader):
-    """A reader for the DHT22 sensor, which attempts to make good readings."""
-
-    def __init__(self, pin: t.Any = board.D4):
-        """Initializes a DHT22 reader connected to the specified pin."""
-        self.device = adafruit_dht.DHT22(pin)
-
-    _TEMP_C_EPSILON = 0.51
-    _RH_PCT_EPSILON = 1.01
-    _NEW_READING_SECS = 2.5
-
-    def read(self) -> types.Reading:
-        """Reads a value from the sensor.  This will block until done."""
-        temps: t.List[float] = []
-        humids: t.List[float] = []
-        while True:
-            temp, rh = self._read_once()
-            if _is_reasonable_temp(temp):
-                temps.append(temp)
-            if _is_reasonable_rh(rh):
-                humids.append(rh)
-            try:
-                return types.Reading.from_now(
-                    temp_c=_last_stable(temps, epsilon=self._TEMP_C_EPSILON),
-                    rh_pct=_last_stable(humids, epsilon=self._RH_PCT_EPSILON))
-            except ValueError:
-                # There is not yet enough data to see if the value is stable.
-                # Wait for a new reading.
-                time.sleep(self._NEW_READING_SECS)
-
-    _RETRY_WAIT_SECS = 0.5
-
-    def _read_once(self) -> t.Tuple[float, float]:
-        """Tries very very hard to read a single value from the sensor."""
-        while True:
-            try:
-                temp, rh = self.device.temperature, self.device.humidity
-                if None not in {temp, rh}:
-                    return temp, rh
-            except RuntimeError:
-                pass  # This will happen a lot.  Just try again.
-            time.sleep(self._RETRY_WAIT_SECS)
-
 
 class BME280Reader:
 
@@ -81,39 +36,3 @@
             temp_c=reading.temperature,
             rh_pct=reading.humidity,
             pressure_kpa=reading.pressure / 10)
-
-
-def _last_stable(
-        vals: t.Iterable[float],
-        *,
-        epsilon: float,
-        count: int = 3) -> float:
-    """Returns the last stable value from the given sequence.
-
-    The stable value is defined as the last value for which there are `count`
-    values in the sequence within `epsilon` of some value.  This is used to
-    eliminate misread values in the sequence, usually due to timing issues.
-    The `count` values need not be in sequence.
-
-    The value returned is the median value of the group (or the next-to-median
-    in the case of an even count).
-    """
-    buckets: t.Dict[float, t.List[float]] = {}
-    for v in vals:
-        buckets.setdefault(v, [])
-        for bucket, bucket_vals in buckets.items():
-            if abs(v - bucket) <= epsilon:
-                bucket_vals.append(v)
-            if len(bucket_vals) == count:
-                return sorted(bucket_vals)[count // 2]
-    raise ValueError('No stable value.')
-
-
-def _is_reasonable_temp(temp: float) -> bool:
-    """True if the temperature is a reasonable level for the boathouse."""
-    return -40 <= temp <= 60
-
-
-def _is_reasonable_rh(rh: float) -> bool:
-    """True if the relative humidity is plausible."""
-    return 0 <= rh <= 100
--- a/weatherlog/reader_test.py	Mon Jan 20 23:36:23 2020 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,51 +0,0 @@
-import itertools
-import unittest
-
-from . import reader
-
-
-class ReasonableTest(unittest.TestCase):
-
-    def test_temp(self):
-        for good_temp in (-40, 0, 20, 36.4, 40, 60):
-            self.assertTrue(reader._is_reasonable_temp(good_temp))
-        for bad_temp in (-273, -41, 69):
-            self.assertFalse(reader._is_reasonable_temp(bad_temp))
-
-    def test_rh(self):
-        for good_rh in (0, 10.0, 55.5, 90):
-            self.assertTrue(reader._is_reasonable_rh(good_rh))
-        for bad_rh in (-1, 101):
-            self.assertFalse(reader._is_reasonable_rh(bad_rh))
-
-
-class StableTest(unittest.TestCase):
-
-    def test_not_enough(self):
-        with self.assertRaises(ValueError):
-            reader._last_stable((), epsilon=1.0)
-        with self.assertRaises(ValueError):
-            reader._last_stable((1.0,), epsilon=1.0)
-        with self.assertRaises(ValueError):
-            reader._last_stable((1.0, 1.5), epsilon=1.0)
-        with self.assertRaises(ValueError):
-            reader._last_stable((1.0, 1.0, 1.0), epsilon=1.0, count=4)
-
-    def test_wild(self):
-        perms = itertools.permutations((1.0, 99.9, 1.2))
-        for perm in perms:
-            with self.assertRaises(ValueError):
-                reader._last_stable(perm, epsilon=0.5)
-
-    def test_good(self):
-        perms = itertools.permutations((1.0, 1.3, 1.5, 66.6, -1))
-        for perm in perms:
-            self.assertEqual(reader._last_stable(perm, epsilon=1.01), 1.3)
-        perms_less = itertools.permutations((1.0, 1.5))
-        for perm in perms_less:
-            self.assertEqual(
-                reader._last_stable(perm, epsilon=0.51, count=2), 1.5)
-
-
-if __name__ == '__main__':
-    unittest.main()