def __init__(self, config, queue_size=10, debug=False, has_humidifier=False, has_dehumidifier=False, recently_minutes=5.): self.queue = CappedQueue(cap=queue_size) self.config = config self.humidifier_on = False self.dehumidifier_on = False self.has_humidifier = has_humidifier self.has_dehumidifier = has_dehumidifier self.recently_minutes = recently_minutes self.humidifier_enabled_at = None self.dehumidifier_enabled_at = None self.start_humidifier_value = None self.start_dehumidifier_value = None self.last_humidified = now() - timedelta(minutes=recently_minutes) self.last_dehumidified = now() - timedelta(minutes=recently_minutes) self.last_minimum = None self.last_maximum = None self.debug = debug
def __init__(self, config, queue_size=10, debug=False, cool_for_s=20., heat_for_s=20., has_cooler=False, has_heater=False, recently_minutes=5.): self.queue = CappedQueue(cap=queue_size) self.config = config self.debug = debug self.learn_cool_file = "/home/pi/controller_data/lcool.csv" self.learn_heat_file = "/home/pi/controller_data/lheat.csv" self.cool_for_s = self.load_cool(cool_for_s) self.heat_for_s = self.load_heat(heat_for_s) self.has_heater = has_heater self.has_cooler = has_cooler self.cooling_on = False self.heating_on = False self.cooler_enabled_at = None self.heater_enabled_at = None self.last_cooling = now() - timedelta(minutes=recently_minutes) self.last_heating = now() - timedelta(minutes=recently_minutes) self.last_minimum = None self.last_maximum = None self.waiting_for_temp_increase = False self.waiting_for_temp_decrease = False self.start_cool_temp = None self.start_heat_temp = None self.recently_minutes = recently_minutes
class Humidity(object): def __init__(self, config, queue_size=10, debug=False, has_humidifier=False, has_dehumidifier=False, recently_minutes=5.): self.queue = CappedQueue(cap=queue_size) self.config = config self.humidifier_on = False self.dehumidifier_on = False self.has_humidifier = has_humidifier self.has_dehumidifier = has_dehumidifier self.recently_minutes = recently_minutes self.humidifier_enabled_at = None self.dehumidifier_enabled_at = None self.start_humidifier_value = None self.start_dehumidifier_value = None self.last_humidified = now() - timedelta(minutes=recently_minutes) self.last_dehumidified = now() - timedelta(minutes=recently_minutes) self.last_minimum = None self.last_maximum = None self.debug = debug def add(self, humidity): if humidity < 0. or humidity > 100.: return self.queue.put(humidity) def change_config(config): self.config = config def average(self): l = self.queue.tolist() return sum(l) / float(len(l)) def humidified_recently(self): """ Have we humidified recently? """ return (now() - self.last_humidified) <= timedelta( minutes=self.recently_minutes) def dehumidified_recently(self): """ Have we dehumidified recently? """ return (now() - self.last_dehumidified) <= timedelta(minutes= self.recently_minutes) def update(self): h = self.average() if self.last_maximum is None: self.last_maximum = h if self.last_minimum is None: self.last_minimum = h self.last_maximum = max(h, self.last_maximum) self.last_minimum = min(h, self.last_minimum) log.debug('h=%.02f max=%.02f', h, self.config.max_humidity) if self.humidifier_on: if h < self.config.max_humidity: return self.last_humidified = now() self.humidifier_on = False elif self.dehumidifier_on: if h > self.config.min_humidity: return self.last_dehumidified = now() self.dehumidifier_on = False elif h <= self.config.min_humidity: if self.dehumidified_recently(): return if not self.has_humidifier: return # if it's dry and we weren't just running the dehumidifier, turn # our humidifier on self.humidifier_on = True self.humidifier_enabled_at = now() self.last_maximum = h self.start_humidifier_value = h elif h >= self.config.max_humidity: if self.humidified_recently(): return if not self.has_dehumidifier: return # if it's humid and we weren't just running the humidifier, turn # our dehumidifier on self.dehumidifier_on = True self.dehumidifier_enabled_at = now() self.last_minimum = h self.start_dehumidifier_value = h else: pass def __str__(self): return "humidity={:.1f}%".format(self.average())
class Temperature(object): def __init__(self, config, queue_size=10, debug=False, cool_for_s=20., heat_for_s=20., has_cooler=False, has_heater=False, recently_minutes=5.): self.queue = CappedQueue(cap=queue_size) self.config = config self.debug = debug self.learn_cool_file = "/home/pi/controller_data/lcool.csv" self.learn_heat_file = "/home/pi/controller_data/lheat.csv" self.cool_for_s = self.load_cool(cool_for_s) self.heat_for_s = self.load_heat(heat_for_s) self.has_heater = has_heater self.has_cooler = has_cooler self.cooling_on = False self.heating_on = False self.cooler_enabled_at = None self.heater_enabled_at = None self.last_cooling = now() - timedelta(minutes=recently_minutes) self.last_heating = now() - timedelta(minutes=recently_minutes) self.last_minimum = None self.last_maximum = None self.waiting_for_temp_increase = False self.waiting_for_temp_decrease = False self.start_cool_temp = None self.start_heat_temp = None self.recently_minutes = recently_minutes def load_cool(self, default_seconds=45.): return load(self.learn_cool_file, default_seconds, self.config.min_temp_f) def save_cool(self, seconds, starting_temp, resulting_temp): save(self.learn_cool_file, starting_temp, self.config.min_temp_f, resulting_temp, seconds) def load_heat(self, default_seconds=45.): return load(self.learn_heat_file, default_seconds, self.config.max_temp_f) def save_heat(self, seconds, starting_temp, resulting_temp): save(self.learn_heat_file, starting_temp, self.config.max_temp_f, resulting_temp, seconds) def add(self, temperature): """ Add a temperature to the queue. """ if temperature < 0. or temperature > 110.: return self.queue.put(temperature) def change_config(config): """ Update the config. """ self.config = config def temperature_average_f(self): """ Get the average temperature from the queue. """ l = self.queue.tolist() return sum(l) / float(len(l)) def cooling_for(self): """ How long we've been cooling for. """ if self.cooler_enabled_at is None: return None return now() - self.cooler_enabled_at def heating_for(self): """ How long we've been heating for. """ if self.heater_enabled_at is None: return None return now() - self.heater_enabled_at def cooled_recently(self, minutes=None): """ Have we cooled recently? """ minutes = self.recently_minutes if minutes is None else minutes return (now() - self.last_cooling) <= timedelta(minutes=minutes) def heated_recently(self, minutes=None): """ Have we heated recently? """ minutes = self.recently_minutes if minutes is None else minutes return (now() - self.last_heating) <= timedelta(minutes=minutes) def update(self): """ Update our flags. This enables / disables heating and cooling given our queue of temperatures. """ t = self.temperature_average_f() if self.last_maximum is None: self.last_maximum = t if self.last_minimum is None: self.last_minimum = t self.last_maximum = max(t, self.last_maximum) self.last_minimum = min(t, self.last_minimum) if self.cooling_on: secs_reached = self.cooling_for() >= timedelta(seconds=self.cool_for_s) overshooting = t < self.config.min_temp_f # overshooting the temp if secs_reached or overshooting: self.last_cooling = now() self.cooling_on = False self.waiting_for_temp_increase = True # we're overshooting the temp, so set the time to cool for # equal to the current elapsed time if overshooting: self.cool_for_s = self.cooling_for() log.info("overshooting, cool_for_s now %s", self.cool_for_s) elif self.heating_on: secs_reached = self.heating_for() >= timedelta(seconds=self.heat_for_s) overshooting = t > self.config.max_temp_f # overshooting the temp if secs_reached or overshooting: self.last_heating = now() self.heating_on = False self.waiting_for_temp_decrease = True # we're overshooting the temp, so set the time to cool for # equal to the current elapsed time if overshooting: self.heat_for_s = self.heating_for() log.info("overshooting, heat_for_s now %s", self.heat_for_s) else: # -------------------------------- # not currently heating or cooling # -------------------------------- if self.waiting_for_temp_increase: # we just ran the cooler and are waiting for the temp to increase if self.cooled_recently(1.): return if t < self.last_minimum + .2: return self.waiting_for_temp_increase = False log.debug('temp is now increasing. min=%.02f', self.last_minimum) cool_for, diff = learn( save=self.save_cool, current_time_s=self.cool_for_s, starting_value=self.start_cool_temp, starting_threshold=self.config.max_temp_f, pad=self.config.temp_pad, target=self.config.min_temp_f, actual=self.last_minimum, debug=self.debug, multiplier=3.0, increasing=False) log.debug( 'last_s=%.01f run_s=%.01f target=%.02f actual=%.02f', self.cool_for_s, cool_for, self.config.min_temp_f, self.last_minimum) # update the number of seconds to cool for min_cool_time_s = 10. max_cool_time_s = 60. * 5. self.cool_for_s = clip( cool_for - diff, min_cool_time_s, max_cool_time_s) elif self.waiting_for_temp_decrease: # we just ran the heater and are waiting for the temp to decrease if self.heated_recently(1.): return if t > self.last_maximum - .2: return self.waiting_for_temp_decrease = False log.debug('temp is now decreasing. max=%.02f', self.last_maximum) heat_for, diff = learn( save=self.save_heat, current_time_s=self.heat_for_s, starting_value=self.start_heat_temp, starting_threshold=self.config.min_temp_f, pad=self.config.temp_pad, target=self.config.max_temp_f, actual=self.last_maximum, debug=self.debug, multiplier=3.0, increasing=True) log.debug( 'last_s=%.01f run_s=%.01f target=%.02f actual=%.02f', self.heat_for_s, heat_for, self.config.max_temp_f, self.last_maximum) # update the number of seconds to heat for min_heat_time_s = 3. max_heat_time_s = 60. * 5. self.heat_for_s = clip( heat_for + diff, min_heat_time_s, max_heat_time_s) elif t >= self.config.max_temp_f: if self.heated_recently(): return if self.cooled_recently(2.5): return if not self.has_cooler: return # if it's warm and we weren't just running a heater, turn # our cooling on self.cooling_on = True self.cooler_enabled_at = now() self.last_minimum = t self.start_cool_temp = t min_cool_time_s = 10. max_cool_time_s = 60. * 5. self.cool_for_s = clip( time_boost( self.cool_for_s, t, self.config.max_temp_f, self.config.temp_pad, increasing=False), min_cool_time_s, max_cool_time_s) log.debug('cooling for %.02fs', self.cool_for_s) elif t <= self.config.min_temp_f: if self.heated_recently(2.5): return if self.cooled_recently(): return if not self.has_heater: return # if it's cool and we weren't just running a cooler, turn # our heating on self.heating_on = True self.heater_enabled_at = now() self.last_maximum = t self.start_heat_temp = t min_heat_time_s = 3. max_heat_time_s = 60. * 5. self.heat_for_s = clip( time_boost( self.heat_for_s, t, self.config.min_temp_f, self.config.temp_pad, increasing=True), min_heat_time_s, max_heat_time_s) if not self.debug: log.debug('heating for %.02fs', self.heat_for_s) else: pass def __str__(self): return "temp*f={:.1f}".format(self.temperature_average_f())