diff --git a/config.json.sample b/config.json.sample index eb6e836..159ed7d 100644 --- a/config.json.sample +++ b/config.json.sample @@ -6,31 +6,33 @@ "contours": [ { "sensor": "/devices/workroom_weather/controls/t", - "control": "/devices/wb-gpio/controls/EXT1_R3A1" + "control": "/devices/wb-gpio/controls/EXT1_R3A6" }, { "sensor": "/devices/bathroom_weather/controls/t", - "control": "/devices/wb-gpio/controls/EXT1_R3A2" - }, - { - "sensor": "/devices/entrance_weather/controls/t", - "control": "/devices/wb-gpio/controls/EXT1_R3A3" - }, - { - "sensor": "/devices/corridor_weather/controls/t", - "control": "/devices/wb-gpio/controls/EXT1_R3A4" - }, - { - "sensor": "/devices/techroom_weather/controls/t", "control": "/devices/wb-gpio/controls/EXT1_R3A5" }, + { + "sensor": "/devices/entrance_weather/controls/t", + "control": "/devices/wb-gpio/controls/EXT1_R3A1" + }, + { + "sensor": "/devices/corridor_weather/controls/t", + "control": "/devices/wb-gpio/controls/EXT1_R3A2" + }, + { + "sensor": "/devices/techroom_weather/controls/t", + "control": "/devices/wb-gpio/controls/EXT1_R3A4" + }, { "sensor": "/devices/kitchen_weather/controls/t", - "control": "/devices/wb-gpio/controls/EXT1_R3A6" + "control": "/devices/wb-gpio/controls/EXT1_R3A3" } ], "target_temperature": 21.0, "hysteresis": 0.5, "boiler_exhaust_t": "/devices/wb-w1/controls/28-000000860adf", - "boiler_control": "/devices/wb-gpio/controls/EXT1_R3A8" - } \ No newline at end of file + "pump_control": "/devices/wb-gpio/controls/EXT1_R3A8", + "boiler_control": "/devices/wb-gpio/controls/EXT1_R3A7" + } + \ No newline at end of file diff --git a/contour.py b/contour.py index 86b617a..1121e69 100644 --- a/contour.py +++ b/contour.py @@ -8,10 +8,12 @@ class Contour: value_time = 0 state = None state_change_time = 0 + target_value = None def __init__(self, config, logger): self.sensor_topic = config['sensor'] self.out_topic = config['control'] + self.target_topic = config['target'] self.logger = logger def update(self, value): @@ -29,4 +31,8 @@ class Contour: if (self.state is None) or (time.time() - self.state_change_time > self.CHANGE_FREQ): self.state = state_value self.logger.info("Contour " + self.sensor_topic + " changed state to " + str(self.state)) - \ No newline at end of file + + def update_target(self, target_value): + self.target_value = target_value + + diff --git a/heat_control.py b/heat_control.py index 5c6b9ca..f4453a1 100644 --- a/heat_control.py +++ b/heat_control.py @@ -43,7 +43,7 @@ logger = logging.getLogger(__name__) def load_config(): """Load configuration from file""" - global config, contours, target_temp, boiler_control, hysteresis + global config, contours, target_temp, boiler_control, hysteresis, pump_control try: with open(CONFIG_FILE, 'r') as f: config = json.load(f) @@ -52,6 +52,7 @@ def load_config(): target_temp = float(config.get('target_temperature')) hysteresis = float(config.get('hysteresis')) boiler_control = config.get('boiler_control') + pump_control = config.get('pump_control') if (boiler_control is None): logger.error("Boiler control not configured") exit(1) @@ -74,6 +75,7 @@ def on_connect(client, userdata, flags, rc): # Subscribe to all temperature sensor topics for k in contours: client.subscribe(k.sensor_topic) + client.subscribe(k.target_topic) logger.info(f"Subscribed to: {k.sensor_topic}") # and boiler exhaust topic client.subscribe(config.get('boiler_exhaust_t')) @@ -89,6 +91,10 @@ def on_message(client, userdata, msg): temperature = float(msg.payload.decode('utf-8')) k.update(temperature) logger.debug(f"Updated {k.sensor_topic}: {temperature}°C") + if (k.target_topic == msg.topic): + temperature = float(msg.payload.decode('utf-8')) + k.update_target(temperature) + logger.debug(f"Updated target {k.target_topic}: {temperature}°C") if msg.topic == config.get('boiler_exhaust_t'): boiler_exhaust_t = float(msg.payload.decode('utf-8')) logger.debug(f"Updated boiler exhaust: {boiler_exhaust_t}°C") @@ -106,12 +112,14 @@ def check_temperatures(): # check boiler exhaust, turn off if too big if (boiler_exhaust_t > BOILER_EXHAUST_LIMIT): logger.error("Boiler exhaust overheat") - # will open all contours (to force coolant circulation) + # will open all contours (to force coolant circulation), + # turn on the pump # and turn off the boiler of course for c in contours: # valves are normally-opened, so 0 means "open" mqtt_client.publish(c.out_topic, "0", retain=True) mqtt_client.publish(boiler_control, "0", retain=True) + mqtt_client.publish(pump_control, "1", retain=True) return # sum_t = 0 @@ -137,10 +145,13 @@ def check_temperatures(): # sensor is offline if contour_t is None: contour_t = sum_t / count_t - if contour_t < target_temp - hysteresis: + target_t = target_temp + if not c.target_value is None: + target_t = c.target_value + if contour_t < target_t - hysteresis: # should turn on contour c.check_state(True) - if contour_t > target_temp + hysteresis: + if contour_t > target_t + hysteresis: # should turn off contour c.check_state(False) if c.state: @@ -161,8 +172,10 @@ def check_temperatures(): if any_opened and time.time() - any_opened_start > BOILER_TURN_ON_AFTER: mqtt_client.publish(boiler_control, "1", retain=True) + mqtt_client.publish(pump_control, "1", retain=True) else: mqtt_client.publish(boiler_control, "0", retain=True) + mqtt_client.publish(pump_control, "0", retain=True) def main_cycle(): """Main 5-second cycle"""