Skip to content

Commit 5c34218

Browse files
committed
Add filter to sensors
1 parent ee30605 commit 5c34218

2 files changed

Lines changed: 60 additions & 3 deletions

File tree

lib/device.py

Lines changed: 58 additions & 1 deletion
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
@@ -178,6 +180,13 @@ def _handle_login_response(self, data):
178180
def _handle_data_response(self, data):
179181
if len(data) == 18:
180182
meas = Measurement(data)
183+
if self._measurements_filter is None:
184+
self._measurements_filter = MeasOutlierFilter(meas.ph, meas.orp)
185+
else:
186+
self._measurements_filter.add(meas.ph, meas.orp)
187+
meas.add_filtered(
188+
self._measurements_filter.get_ph(), self._measurements_filter.get_orp()
189+
)
181190
_LOGGER.debug("Adding result: %s" % meas)
182191
self._measurements.append(meas)
183192
self._latest_measurement = meas
@@ -240,6 +249,46 @@ def __exit__(self, type, value, traceback):
240249
self.close()
241250

242251

252+
class MeasOutlierFilter:
253+
def __init__(self, ph: float, orp: float, history: int = 10) -> None:
254+
self._ph_filter = OutlierFilter(ph, history)
255+
self._orp_filter = OutlierFilter(orp, history)
256+
257+
def add(self, ph, orp):
258+
self._ph_filter.add(ph)
259+
self._orp_filter.add(orp)
260+
261+
def get_ph(self) -> float:
262+
return self._ph_filter.get()
263+
264+
def get_orp(self) -> float:
265+
return self._orp_filter.get()
266+
267+
268+
class OutlierFilter:
269+
def __init__(self, init_value: float, history: int = 10) -> None:
270+
self._values = []
271+
self._values.append(init_value)
272+
self._history = history
273+
274+
def add(self, value):
275+
self._values.append(value)
276+
if len(self._values) > self._history:
277+
self._values.pop(0)
278+
279+
def get(self) -> float:
280+
try:
281+
stddev_val = stdev(self._values)
282+
mean_val = mean(self._values)
283+
for val in reversed(self._values):
284+
if (val <= mean_val + stddev_val) and (val >= mean_val - stddev_val):
285+
return val
286+
except StatisticsError:
287+
return self._values[0]
288+
_LOGGER.error("No match in outlier filter shall never happen!")
289+
return None
290+
291+
243292
class Measurement:
244293
def __init__(self, data) -> None:
245294
flag1 = data[8]
@@ -249,17 +298,25 @@ def __init__(self, data) -> None:
249298
self.ph_on = flag2 & 0b0000_0001 != 0
250299
ph_raw = data[10:12]
251300
self.ph = int.from_bytes(ph_raw, "big") * 0.01
301+
self.ph_filt = None
252302
orp_raw = data[12:14]
253303
self.orp = int.from_bytes(orp_raw, "big") - 2000
304+
self.orp_filt = None
254305
unknown1_raw = data[14:16]
255306
self.unknown1 = int.from_bytes(unknown1_raw, "big")
256307
unknown2_raw = data[15:18]
257308
self.unknown2 = int.from_bytes(unknown2_raw, "big")
258309

310+
def add_filtered(self, ph_filt, orp_filt):
311+
self.ph_filt = ph_filt
312+
self.orp_filt = orp_filt
313+
259314
def __str__(self) -> str:
260-
return "pH: %s, Orp: %s, In-water: %s, pH-on: %s, Orp-on: %s" % (
315+
return "pH: %s (%s), Orp: %s (%s), In-water: %s, pH-on: %s, Orp-on: %s" % (
261316
self.ph,
317+
self.ph_filt,
262318
self.orp,
319+
self.orp_filt,
263320
self.in_water,
264321
self.ph_on,
265322
self.orp_on,

sensor.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,10 @@ def __init__(
4242

4343

4444
SENSORS = [
45-
DeviceSensorConfig("PH-803W pH", "ph", "mdi:water-percent", ""),
45+
DeviceSensorConfig("PH-803W pH", "ph_filt", "mdi:water-percent", ""),
4646
DeviceSensorConfig(
4747
"PH-803W ORP",
48-
"orp",
48+
"orp_filt",
4949
"mdi:water-opacity",
5050
ELECTRIC_POTENTIAL_MILLIVOLT,
5151
SensorDeviceClass.VOLTAGE,

0 commit comments

Comments
 (0)