Mercurial > personal > weatherlog
annotate weatherlog/reader.py @ 3:d961c8a93d6b
reader: Use floor-division for list index.
author | Paul Fisher <paul@pfish.zone> |
---|---|
date | Fri, 27 Sep 2019 21:38:23 -0400 |
parents | 9310e2ff7e17 |
children | 885bff085edf |
rev | line source |
---|---|
0
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
1 """A module for reading data from the actual temperature sensor. |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
2 |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
3 You should probably assume that this is wildly thread-unsafe. |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
4 """ |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
5 |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
6 import abc |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
7 import datetime |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
8 import time |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
9 import typing as t |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
10 |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
11 import adafruit_dht |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
12 import attr |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
13 import board |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
14 import pytz |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
15 |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
16 |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
17 def _utc_now() -> datetime.datetime: |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
18 """utcnow, but timezone-aware.""" |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
19 return datetime.datetime.utcnow().replace(tzinfo=pytz.UTC) |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
20 |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
21 |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
22 @attr.s(frozen=True, slots=True) |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
23 class Reading(object): |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
24 """A single reading from a temperature/humidity sensor.""" |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
25 |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
26 # The temperature, in degrees Celsius. |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
27 temp_c = attr.ib(type=float) |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
28 |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
29 # The relative humidity, in percent. |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
30 rh_pct = attr.ib(type=float) |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
31 |
1
b52600fade25
reader: Fix attr order in Reading.
Paul Fisher <paul@pfish.zone>
parents:
0
diff
changeset
|
32 # The timestamp of the reading. |
b52600fade25
reader: Fix attr order in Reading.
Paul Fisher <paul@pfish.zone>
parents:
0
diff
changeset
|
33 timestamp = attr.ib(type=datetime.datetime, factory=_utc_now) |
b52600fade25
reader: Fix attr order in Reading.
Paul Fisher <paul@pfish.zone>
parents:
0
diff
changeset
|
34 |
0
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
35 |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
36 class Reader(metaclass=abc.ABCMeta): |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
37 """Interface for a thing which reads temperatures.""" |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
38 |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
39 @abc.abstractmethod |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
40 def read(self) -> Reading: |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
41 """Reads a value from the weather sensor.""" |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
42 raise NotImplementedError() |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
43 |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
44 |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
45 class DHT22Reader(Reader): |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
46 """A reader for the DHT22 sensor, which attempts to make good readings.""" |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
47 |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
48 def __init__(self, pin: t.Any = board.D4): |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
49 """Initializes a DHT22 reader connected to the specified pin.""" |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
50 self.device = adafruit_dht.DHT22(pin) |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
51 |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
52 _TEMP_C_EPSILON = 0.51 |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
53 _RH_PCT_EPSILON = 1.01 |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
54 _NEW_READING_SECS = 2.5 |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
55 |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
56 def read(self) -> Reading: |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
57 """Reads a value from the sensor. This will block until done.""" |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
58 temps: t.List[float] = [] |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
59 humids: t.List[float] = [] |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
60 while True: |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
61 temp, rh = self._read_once() |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
62 if _is_reasonable_temp(temp): |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
63 temps.append(temp) |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
64 if _is_reasonable_rh(rh): |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
65 humids.append(rh) |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
66 try: |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
67 return Reading( |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
68 temp_c=_last_stable(temps, epsilon=self._TEMP_C_EPSILON), |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
69 rh_pct=_last_stable(humids, epsilon=self._RH_PCT_EPSILON)) |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
70 except ValueError: |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
71 # There is not yet enough data to see if the value is stable. |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
72 # Wait for a new reading. |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
73 time.sleep(self._NEW_READING_SECS) |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
74 |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
75 _RETRY_WAIT_SECS = 0.5 |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
76 |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
77 def _read_once(self) -> t.Tuple[float, float]: |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
78 """Tries very very hard to read a single value from the sensor.""" |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
79 while True: |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
80 try: |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
81 temp, rh = self.device.temperature, self.device.humidity |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
82 if None not in {temp, rh}: |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
83 return temp, rh |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
84 except RuntimeError: |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
85 pass # This will happen a lot. Just try again. |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
86 time.sleep(self._RETRY_WAIT_SECS) |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
87 |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
88 |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
89 def _last_stable( |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
90 vals: t.Iterable[float], |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
91 *, |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
92 epsilon: float, |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
93 count: int = 3) -> float: |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
94 """Returns the last stable value from the given sequence. |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
95 |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
96 The stable value is defined as the last value for which there are `count` |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
97 values in the sequence within `epsilon` of some value. This is used to |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
98 eliminate misread values in the sequence, usually due to timing issues. |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
99 The `count` values need not be in sequence. |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
100 |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
101 The value returned is the median value of the group (or the next-to-median |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
102 in the case of an even count). |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
103 """ |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
104 buckets: t.Dict[float, t.List[float]] = {} |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
105 for v in vals: |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
106 buckets.setdefault(v, []) |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
107 for bucket, bucket_vals in buckets.items(): |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
108 if abs(v - bucket) <= epsilon: |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
109 bucket_vals.append(v) |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
110 if len(bucket_vals) == count: |
3
d961c8a93d6b
reader: Use floor-division for list index.
Paul Fisher <paul@pfish.zone>
parents:
2
diff
changeset
|
111 return sorted(bucket_vals)[count // 2] |
2
9310e2ff7e17
reader: Actually throw ValueError from _last_stable.
Paul Fisher <paul@pfish.zone>
parents:
1
diff
changeset
|
112 raise ValueError('No stable value.') |
0
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
113 |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
114 |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
115 def _is_reasonable_temp(temp: float) -> bool: |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
116 """True if the temperature is a reasonable level for the boathouse.""" |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
117 return -40 <= temp <= 60 |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
118 |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
119 |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
120 def _is_reasonable_rh(rh: float) -> bool: |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
121 """True if the relative humidity is plausible.""" |
18dc6245c91a
Create initial version of weather sensor reader.
Paul Fisher <paul@pfish.zone>
parents:
diff
changeset
|
122 return 0 <= rh <= 100 |