Skip to content

Commit 5fb9b71

Browse files
authored
Merge pull request #15 from dala318/filter_sensors
Add filter to sensors
2 parents e57e234 + 27cf96f commit 5fb9b71

2 files changed

Lines changed: 55 additions & 17 deletions

File tree

__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ def shutdown(event):
114114

115115
try:
116116
self.device_client.run(once=False)
117-
except (device.DeviceError, ConnectionError):
117+
except (device.DeviceError, RecursionError, ConnectionError):
118118
_LOGGER.exception("Failed to read data, attempting to recover")
119119
self.device_client.close()
120120
self._fails += 1

lib/device.py

Lines changed: 54 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
"""A PH-803W device value collector."""
2+
from statistics import stdev, mean, StatisticsError
23
import threading
34
import socket
45
import logging
@@ -23,6 +24,7 @@ def __init__(self, host):
2324
self.passcode = ""
2425
self._measurements = []
2526
self._latest_measurement = None
27+
self._measurements_filter = None
2628
self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
2729
self._loop = True
2830
self._empty_counter = 0
@@ -159,43 +161,39 @@ def _handle_response(self, data):
159161
elif message_type == 0x94:
160162
self._handle_data_extended_response(data)
161163
else:
162-
pass
163164
_LOGGER.warning(
164165
"Ignore data package because invalid message type %s" % message_type
165166
)
166167

167168
def _handle_passcode_response(self, data):
168-
pass
169169
_LOGGER.warning("Passcode resonse ignored")
170170

171171
def _handle_login_response(self, data):
172-
pass
173172
_LOGGER.warning("Login resonse ignored")
174173

175174
def _handle_data_response(self, data):
176175
if len(data) == 18:
177176
meas = Measurement(data)
177+
if self._measurements_filter is None:
178+
self._measurements_filter = MeasOutlierFilter(meas.ph, meas.orp)
179+
else:
180+
self._measurements_filter.add(meas.ph, meas.orp)
181+
meas.add_filtered(
182+
self._measurements_filter.get_ph(), self._measurements_filter.get_orp()
183+
)
178184
_LOGGER.debug("Adding result: %s" % meas)
179185
self._measurements.append(meas)
180186
self._latest_measurement = meas
181187
if len(self._measurements) > 100:
182188
self._measurements.pop(0)
183189
for callback in self._callbacks:
184190
callback()
185-
else:
186-
pass
187-
_LOGGER.debug(meas)
191+
_LOGGER.debug(meas)
188192

189193
def _handle_data_extended_response(self, data):
190-
pass
191194
_LOGGER.warning("Extended data ignored")
192195

193196
def _handle_ping_pong_response(self):
194-
# if self._pong_thread is None or self._pong_thread.done:
195-
# self._pong_thread = asyncio.create_task(self._async_queue_ping())
196-
# pass
197-
# else:
198-
# _LOGGER.debug("Pong thread alredy running")
199197
_LOGGER.debug("Pong message received")
200198

201199
def _send_ping(self):
@@ -208,10 +206,6 @@ def _ping_loop(self):
208206
self._send_ping()
209207
sleep(PH803W_PING_INTERVAL)
210208

211-
# async def _async_queue_ping(self):
212-
# await asyncio.sleep(PH803W_PING_INTERVAL)
213-
# self._send_ping()
214-
215209
def abort(self):
216210
self._loop = False
217211

@@ -237,6 +231,46 @@ def __exit__(self, type, value, traceback):
237231
self.close()
238232

239233

234+
class MeasOutlierFilter:
235+
def __init__(self, ph: float, orp: float, history: int = 10) -> None:
236+
self._ph_filter = OutlierFilter(ph, history)
237+
self._orp_filter = OutlierFilter(orp, history)
238+
239+
def add(self, ph: float, orp: float) -> None:
240+
self._ph_filter.add(ph)
241+
self._orp_filter.add(orp)
242+
243+
def get_ph(self) -> float:
244+
return self._ph_filter.get()
245+
246+
def get_orp(self) -> float:
247+
return self._orp_filter.get()
248+
249+
250+
class OutlierFilter:
251+
def __init__(self, init_value: float, history: int = 10) -> None:
252+
self._values = []
253+
self._values.append(init_value)
254+
self._history = history
255+
256+
def add(self, value: float) -> None:
257+
self._values.append(value)
258+
if len(self._values) > self._history:
259+
self._values.pop(0)
260+
261+
def get(self) -> float:
262+
try:
263+
stddev_val = stdev(self._values)
264+
mean_val = mean(self._values)
265+
for val in reversed(self._values):
266+
if (val <= mean_val + stddev_val) and (val >= mean_val - stddev_val):
267+
return val
268+
except StatisticsError:
269+
return self._values[-1]
270+
_LOGGER.error("No match in outlier filter shall never happen!")
271+
return self._values[-1]
272+
273+
240274
class Measurement:
241275
def __init__(self, data) -> None:
242276
flag1 = data[8]
@@ -253,6 +287,10 @@ def __init__(self, data) -> None:
253287
unknown2_raw = data[15:18]
254288
self.unknown2 = int.from_bytes(unknown2_raw, "big")
255289

290+
def add_filtered(self, ph_filt: float, orp_filt: float) -> None:
291+
self.ph = ph_filt
292+
self.orp = orp_filt
293+
256294
def __str__(self) -> str:
257295
return "pH: %s, Orp: %s, In-water: %s, pH-on: %s, Orp-on: %s" % (
258296
self.ph,

0 commit comments

Comments
 (0)