From ef2582dfa77c6fcacd4873bfe8ac53edb4a951d4 Mon Sep 17 00:00:00 2001 From: Thomas Klaehn Date: Thu, 30 Aug 2018 13:57:01 +0200 Subject: [PATCH] chickenhouse: add climate control Signed-off-by: Thomas Klaehn --- chickenhouse.service | 4 +- chickenhouse/__init__.py | 31 ++++++++++++ climate_control/__init__.py | 95 +++++++++++++++++++++++++++++++++++++ climate_control/heat.py | 19 ++++++++ climate_control/sensors.py | 28 +++++++++++ gate_guard/__init__.py | 24 ---------- setup.py | 4 +- 7 files changed, 177 insertions(+), 28 deletions(-) create mode 100644 chickenhouse/__init__.py create mode 100644 climate_control/__init__.py create mode 100644 climate_control/heat.py create mode 100644 climate_control/sensors.py diff --git a/chickenhouse.service b/chickenhouse.service index ed53bc8..1e6f4f4 100644 --- a/chickenhouse.service +++ b/chickenhouse.service @@ -4,7 +4,7 @@ After=multi-user.target [Service] Type=idle -ExecStart=/usr/bin/python /usr/local/lib/python2.7/dist-packages/gate_guard/__init__.py +ExecStart=/usr/bin/python /usr/local/lib/python2.7/dist-packages/chickenhouse/__init__.py [Install] -WantedBy=multi-user.target \ No newline at end of file +WantedBy=multi-user.target diff --git a/chickenhouse/__init__.py b/chickenhouse/__init__.py new file mode 100644 index 0000000..45b2e93 --- /dev/null +++ b/chickenhouse/__init__.py @@ -0,0 +1,31 @@ +'''Created on Aug 30, 2018 +@author: tkl +''' + +import time +import sys +import logging +import gate_guard.gate +from climate_control import ClimateControl + +LOGFILE = '/var/log/chickenhouse.log' +LOGLEVEL = logging.DEBUG +LOGFORMAT = '%(asctime)s %(message)s' + +def main(): + logging.basicConfig(filename=LOGFILE, level=LOGLEVEL, format=LOGFORMAT) + gate_state = gate_guard.gate.Gate() + climate_control = ClimateControl() + try: + climate_control.start() + while True: + gate_state.poll() + time.sleep(0.1) + + except KeyboardInterrupt: + climate_control.stop() + print "key exit" + return None + +if __name__ == "__main__": + sys.exit(main()) diff --git a/climate_control/__init__.py b/climate_control/__init__.py new file mode 100644 index 0000000..78af656 --- /dev/null +++ b/climate_control/__init__.py @@ -0,0 +1,95 @@ +''' +Created on Aug 30, 2018 + +@author: tkl +''' +import logging +import socket +import ssl +import threading +import time +import paho.mqtt.client as mqtt +from climate_control.sensors import Dht22 +from climate_control.heat import Heat + +MQTT_BROKER = "mqtt.blackfinn.de" +MQTT_PORT = 8883 +MQTT_CERT = "/etc/ssl/certs/DST_Root_CA_X3.pem" +MQTT_TEMPERATURE_TOPIC = "outdoor/chickenhouse/temperature" +MQTT_HUMIDITY_TOPIC = "outdoor/chickenhouse/humidity" +MQTT_ROOM_HEAT_TOPIC = "outdoor/chickenhouse/heat/room" +MQTT_WATER_HEAT_TOPIC = "outdoor/chickenhouse/heat/water" + +WATER_HEAT_THRESHOLD = {"on": 1.0, "off": 2.0} +ROOM_HEAT_THRESHOLD = {"on": 1.0, "off": 2.0} + +STATE_OFF = "off" +STATE_ON = "ON" + +class ClimateControl(threading.Thread): + __water_heat = Heat(20) + __room_heat = Heat(21) + __climate = Dht22(26) + __thread_condition = True + __now = time.time() + __interval = 15 * 60 + __next = time.time() + __water_heat_state = STATE_OFF + __room_heat_state = STATE_OFF + + def __init__(self): + super(ClimateControl, self).__init__() + + def __check_switch_water_heat(self, temperature): + ret = False + if temperature >= WATER_HEAT_THRESHOLD["off"] and self.__water_heat_state == STATE_ON: + self.__water_heat.off() + self.__water_heat_state = STATE_OFF + ret = True + elif temperature <= WATER_HEAT_THRESHOLD["on"] and self.__water_heat_state == STATE_OFF: + self.__water_heat.on() + self.__water_heat_state = STATE_ON + ret = True + return ret + + def __check_switch_room_heat(self, temperature): + ret = False + if temperature >= ROOM_HEAT_THRESHOLD["off"] and self.__room_heat_state == STATE_ON: + self.__room_heat.off() + self.__room_heat_state = STATE_OFF + ret = True + elif temperature <= ROOM_HEAT_THRESHOLD["on"] and self.__room_heat_state == STATE_OFF: + self.__room_heat.on() + self.__room_heat_state = STATE_ON + ret = True + return ret + + def run(self): + while self.__thread_condition: + self.__now = time.time() + if self.__next <= self.__now: + humidity, temperature = self.__climate.read() + try: + client = mqtt.Client() + client.tls_set(MQTT_CERT) + client.connect(MQTT_BROKER, MQTT_PORT, 60) + client.loop_start() + msg = "{} {}".format(self.__now, temperature) + client.publish(MQTT_TEMPERATURE_TOPIC, msg, qos=2, retain=True) + msg = "{} {}".format(self.__now, humidity) + client.publish(MQTT_HUMIDITY_TOPIC, msg, qos=2, retain=True) + if self.__check_switch_water_heat(temperature): + msg = "{} {}".format(self.__now, self.__water_heat_state) + client.publish(MQTT_WATER_HEAT_TOPIC, msg, qos=2, retain=True) + if self.__check_switch_room_heat(temperature): + msg = "{} {}".format(self.__now, self.__room_heat_state) + client.publish(MQTT_ROOM_HEAT_TOPIC, msg, qos=2, retain=True) + client.loop_stop() + client.disconnect() + except(ValueError, TypeError, socket.error, ssl.CertificateError): + logging.info('unable to publish to mqtt') + self.__next = self.__now + self.__interval + time.sleep(1) + + def stop(self): + self.__thread_condition = False diff --git a/climate_control/heat.py b/climate_control/heat.py new file mode 100644 index 0000000..08d406c --- /dev/null +++ b/climate_control/heat.py @@ -0,0 +1,19 @@ +try: + import gpio +except ImportError: + raise ImportError("gpio not found.") + +class Heat(object): + def __init__(self, pin): + self.pin = gpio.Gpio(pin) + + self.pin.export() + self.pin.direction(gpio.Gpio.DIRECTION_OUT) + + self.pin.write(0) + + def on(self): + self.pin.write(1) + + def off(self): + self.pin.write(0) diff --git a/climate_control/sensors.py b/climate_control/sensors.py new file mode 100644 index 0000000..4fdf007 --- /dev/null +++ b/climate_control/sensors.py @@ -0,0 +1,28 @@ +'''Module for sensor implementations''' +try: + import Adafruit_DHT +except ImportError: + raise ImportError('Adafruit_DHT library not found.') + +def is_valid(temperature, humidity): + '''Check if temperature and humidity are valid.''' + return True if humidity <= 100.0 and humidity >= 0.0 and \ + temperature <= 100.0 and temperature >= -50.0 else False + +class Dht22(object): + '''DHT 22 temperature and Humidity sensor class.''' + sensor = 22 + pin = int() + + def __init__(self, pin): + self.pin = pin + + def read(self): + '''Read temperature and humidity.''' + temperature = -200.0 + humidity = -1.0 + valid = False + while valid is False: + temperature, humidity = Adafruit_DHT.read_retry(self.sensor, self.pin) + valid = is_valid(temperature, humidity) + return temperature, humidity diff --git a/gate_guard/__init__.py b/gate_guard/__init__.py index 4e6221b..e69de29 100644 --- a/gate_guard/__init__.py +++ b/gate_guard/__init__.py @@ -1,24 +0,0 @@ -''' -Created on Dec 19, 2016 - -@author: klaehn -''' -import time -import sys -import logging -import gate_guard.gate - -def main(): - logging.basicConfig(filename='/var/log/gate_guard.log', level=logging.DEBUG, format='%(asctime)s %(message)s') - gate_state = gate_guard.gate.Gate() - try: - while True: - gate_state.poll() - time.sleep(0.1) - - except KeyboardInterrupt: - print "key exit" - return None - -if __name__ == "__main__": - sys.exit(main()) diff --git a/setup.py b/setup.py index 3cb0911..ea4503f 100644 --- a/setup.py +++ b/setup.py @@ -6,11 +6,11 @@ import stat import sys NAME = 'chickenhouse' -VERSION = '1.0.0' +VERSION = '1.1.0' AUTHOR = 'tkl' EMAIL = 'tkl@blackfinn.de' URL = 'https://git.blackfinn.de/python/chickenhouse' -PACKAGES = ['gate_guard'] +PACKAGES = ['chickenhouse', 'climate_control', 'gate_guard'] SCRIPTS = ['chickenhouse.service'] if sys.argv[1] == 'install':