From b23b1e1beedde4c0a0c057222788414053d0cc55 Mon Sep 17 00:00:00 2001 From: Martin Mares Date: Fri, 19 Jul 2019 20:43:29 +0200 Subject: [PATCH] Auto: Averaging --- auto/burrow-auto | 75 +++++++++++++++++++++++++++++++++++++----------- 1 file changed, 59 insertions(+), 16 deletions(-) diff --git a/auto/burrow-auto b/auto/burrow-auto index 4c30f59..12fd420 100755 --- a/auto/burrow-auto +++ b/auto/burrow-auto @@ -12,9 +12,17 @@ def debug(msg): if debug_mode: print(("\t" * indent) + msg) +def diff(x, y): + if x is None or y is None: + return None + else: + return x - y + class State: def __init__(self): - self.attrs = {} + self.attrs = {} + self.hyst_state = {} + self.running_averages = {} def update(self): self.now = time.time() @@ -25,7 +33,6 @@ class State: self.hour = tm.tm_hour self.min = tm.tm_min self.wday = tm.tm_wday - self.hyst_state = {} debug("[{}-{:02d}-{:02d} {:02d}:{:02d}:{:02d} wday={}]".format( self.year, self.month, self.day, self.hour, self.min, tm.tm_sec, @@ -79,6 +86,38 @@ class State: self.hyst_state[key] = new_state return new_state + def update_average(self, key, window_seconds): + if key not in self.running_averages: + self.running_averages[key] = ([], 0, 0, None) + (history, sum, count, avg) = self.running_averages[key] + + while len(history) > 0 and history[0][0] <= self.now - window_seconds: + if history[0][1] is not None: + sum -= history[0][1] + count -= 1 + history.pop(0) + + curr = self.get_sensor(key) + history.append((self.now, curr)) + if curr is not None: + sum += curr + count += 1 + + if count > len(history) // 2: + avg = sum / count + else: + avg = None + + self.running_averages[key] = (history, sum, count, avg) + debug("= avg {:.6} ({} samples, {} non-null)".format(avg, len(history), count)) + if avg is not None: + self.set("avg/" + key, "{:.6} {}".format(avg, int(self.now))) + + def get_sensor_avg(self, key): + val = self.running_averages[key][3] + debug("< {} = avg {:.6}".format(key, val)) + return val + st = State() def on_connect(mq, userdata, flags, rc): @@ -123,10 +162,10 @@ def auto_circ(): def auto_air(): global st - tii = st.get_sensor('air/inside-intake') - tie = st.get_sensor('air/inside-exhaust') - toi = st.get_sensor('air/outside-intake') - tmix = st.get_sensor('air/mixed') + tii = st.get_sensor_avg('air/inside-intake') + tie = st.get_sensor_avg('air/inside-exhaust') + toi = st.get_sensor_avg('air/outside-intake') + tmix = st.get_sensor_avg('air/mixed') house_warm = st.hysteresis('house_warm', tii, 23.5, 24.5) house_hot = st.hysteresis('house_hot', tii, 24.5, 25) @@ -137,20 +176,15 @@ def auto_air(): ac_off = st.hysteresis('ac_off', tmix, tie - 5, tie - 4) # Do we want to bypass the heat exchanger? - if toi is None or tie is None: + outside_warmer = st.hysteresis('outside_warmer', diff(toi, tii), -0.5, 0.5) + if (house_warm > 0) and (outside_warmer > 0) or \ + (house_warm < 0) and (outside_warmer < 0): st.set('air/bypass', 0) else: - outside_warmer = (st.hysteresis('bypass', toi, tii - 0.5, tii + 0.5) >= 0) - if (house_warm >= 0) == outside_warmer: - st.set('air/bypass', 0) - else: - st.set('air/bypass', 1) + st.set('air/bypass', 1) # Is mixed air colder than air from the inside? - if tii is None or tmix is None: - mixed_warmer = 0 - else: - mixed_warmer = st.hysteresis('mixed_warmer', tmix, tii - 1, tii) + mixed_warmer = st.hysteresis('mixed_warmer', diff(tmix, tii), -1, 0) # Do we want to boost heat exchanger fan? if ac_off < 0 or (house_hot > 0 and mixed_warmer < 0): @@ -185,6 +219,15 @@ checks = [ while True: st.update() + + debug("averages") + indent += 1 + st.update_average('air/outside-intake', 60) + st.update_average('air/inside-intake', 60) + st.update_average('air/inside-exhaust', 60) + st.update_average('air/mixed', 60) + indent -= 1 + for name, func in checks: if st.auto_enabled(name): debug(name) -- 2.39.2