Отдельные уставки на каждый контур. Управляем и насосом тоже

This commit is contained in:
ftc
2025-12-14 15:38:49 +03:00
parent 14552403d3
commit 8f35d368e6
3 changed files with 42 additions and 21 deletions
+18 -16
View File
@@ -6,31 +6,33 @@
"contours": [ "contours": [
{ {
"sensor": "/devices/workroom_weather/controls/t", "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", "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" "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", "sensor": "/devices/kitchen_weather/controls/t",
"control": "/devices/wb-gpio/controls/EXT1_R3A6" "control": "/devices/wb-gpio/controls/EXT1_R3A3"
} }
], ],
"target_temperature": 21.0, "target_temperature": 21.0,
"hysteresis": 0.5, "hysteresis": 0.5,
"boiler_exhaust_t": "/devices/wb-w1/controls/28-000000860adf", "boiler_exhaust_t": "/devices/wb-w1/controls/28-000000860adf",
"boiler_control": "/devices/wb-gpio/controls/EXT1_R3A8" "pump_control": "/devices/wb-gpio/controls/EXT1_R3A8",
} "boiler_control": "/devices/wb-gpio/controls/EXT1_R3A7"
}
+7 -1
View File
@@ -8,10 +8,12 @@ class Contour:
value_time = 0 value_time = 0
state = None state = None
state_change_time = 0 state_change_time = 0
target_value = None
def __init__(self, config, logger): def __init__(self, config, logger):
self.sensor_topic = config['sensor'] self.sensor_topic = config['sensor']
self.out_topic = config['control'] self.out_topic = config['control']
self.target_topic = config['target']
self.logger = logger self.logger = logger
def update(self, value): 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): if (self.state is None) or (time.time() - self.state_change_time > self.CHANGE_FREQ):
self.state = state_value self.state = state_value
self.logger.info("Contour " + self.sensor_topic + " changed state to " + str(self.state)) self.logger.info("Contour " + self.sensor_topic + " changed state to " + str(self.state))
def update_target(self, target_value):
self.target_value = target_value
+17 -4
View File
@@ -43,7 +43,7 @@ logger = logging.getLogger(__name__)
def load_config(): def load_config():
"""Load configuration from file""" """Load configuration from file"""
global config, contours, target_temp, boiler_control, hysteresis global config, contours, target_temp, boiler_control, hysteresis, pump_control
try: try:
with open(CONFIG_FILE, 'r') as f: with open(CONFIG_FILE, 'r') as f:
config = json.load(f) config = json.load(f)
@@ -52,6 +52,7 @@ def load_config():
target_temp = float(config.get('target_temperature')) target_temp = float(config.get('target_temperature'))
hysteresis = float(config.get('hysteresis')) hysteresis = float(config.get('hysteresis'))
boiler_control = config.get('boiler_control') boiler_control = config.get('boiler_control')
pump_control = config.get('pump_control')
if (boiler_control is None): if (boiler_control is None):
logger.error("Boiler control not configured") logger.error("Boiler control not configured")
exit(1) exit(1)
@@ -74,6 +75,7 @@ def on_connect(client, userdata, flags, rc):
# Subscribe to all temperature sensor topics # Subscribe to all temperature sensor topics
for k in contours: for k in contours:
client.subscribe(k.sensor_topic) client.subscribe(k.sensor_topic)
client.subscribe(k.target_topic)
logger.info(f"Subscribed to: {k.sensor_topic}") logger.info(f"Subscribed to: {k.sensor_topic}")
# and boiler exhaust topic # and boiler exhaust topic
client.subscribe(config.get('boiler_exhaust_t')) client.subscribe(config.get('boiler_exhaust_t'))
@@ -89,6 +91,10 @@ def on_message(client, userdata, msg):
temperature = float(msg.payload.decode('utf-8')) temperature = float(msg.payload.decode('utf-8'))
k.update(temperature) k.update(temperature)
logger.debug(f"Updated {k.sensor_topic}: {temperature}°C") 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'): if msg.topic == config.get('boiler_exhaust_t'):
boiler_exhaust_t = float(msg.payload.decode('utf-8')) boiler_exhaust_t = float(msg.payload.decode('utf-8'))
logger.debug(f"Updated boiler exhaust: {boiler_exhaust_t}°C") 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 # check boiler exhaust, turn off if too big
if (boiler_exhaust_t > BOILER_EXHAUST_LIMIT): if (boiler_exhaust_t > BOILER_EXHAUST_LIMIT):
logger.error("Boiler exhaust overheat") 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 # and turn off the boiler of course
for c in contours: for c in contours:
# valves are normally-opened, so 0 means "open" # valves are normally-opened, so 0 means "open"
mqtt_client.publish(c.out_topic, "0", retain=True) mqtt_client.publish(c.out_topic, "0", retain=True)
mqtt_client.publish(boiler_control, "0", retain=True) mqtt_client.publish(boiler_control, "0", retain=True)
mqtt_client.publish(pump_control, "1", retain=True)
return return
# #
sum_t = 0 sum_t = 0
@@ -137,10 +145,13 @@ def check_temperatures():
# sensor is offline # sensor is offline
if contour_t is None: if contour_t is None:
contour_t = sum_t / count_t 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 # should turn on contour
c.check_state(True) c.check_state(True)
if contour_t > target_temp + hysteresis: if contour_t > target_t + hysteresis:
# should turn off contour # should turn off contour
c.check_state(False) c.check_state(False)
if c.state: if c.state:
@@ -161,8 +172,10 @@ def check_temperatures():
if any_opened and time.time() - any_opened_start > BOILER_TURN_ON_AFTER: if any_opened and time.time() - any_opened_start > BOILER_TURN_ON_AFTER:
mqtt_client.publish(boiler_control, "1", retain=True) mqtt_client.publish(boiler_control, "1", retain=True)
mqtt_client.publish(pump_control, "1", retain=True)
else: else:
mqtt_client.publish(boiler_control, "0", retain=True) mqtt_client.publish(boiler_control, "0", retain=True)
mqtt_client.publish(pump_control, "0", retain=True)
def main_cycle(): def main_cycle():
"""Main 5-second cycle""" """Main 5-second cycle"""