greenhouse: Initial commit
This commit is contained in:
commit
b2ad105079
10
greenhouse.service
Normal file
10
greenhouse.service
Normal file
@ -0,0 +1,10 @@
|
||||
[Unit]
|
||||
Description=Greenhouse service
|
||||
After=multi-user.target
|
||||
|
||||
[Service]
|
||||
Type=idle
|
||||
ExecStart=/usr/bin/python3 -m greenhouse
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
0
greenhouse/__init__.py
Normal file
0
greenhouse/__init__.py
Normal file
154
greenhouse/__main__.py
Normal file
154
greenhouse/__main__.py
Normal file
@ -0,0 +1,154 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import datetime
|
||||
import logging
|
||||
import sys
|
||||
import threading
|
||||
import time
|
||||
|
||||
import RPi.GPIO as gpio
|
||||
import paho.mqtt.client as mqtt
|
||||
import Adafruit_DHT
|
||||
|
||||
SWITCH_ON_TEMP = 8.0
|
||||
SWITCH_OFF_TEMP = 10.0
|
||||
|
||||
log_level = logging.INFO
|
||||
LOG_FILE = "/var/log/greenhouse.log"
|
||||
LOG_FORMAT = "%(asctime)s %(levelname)s %(message)s"
|
||||
|
||||
#logging.basicConfig(format=LOG_FORMAT, level=log_level, filename=LOG_FILE)
|
||||
logging.basicConfig(format=LOG_FORMAT, level=log_level)
|
||||
log = logging.getLogger('greenhouse')
|
||||
|
||||
class EnvData(object):
|
||||
def __init__(self):
|
||||
self.stamp = datetime.datetime.fromtimestamp(0)
|
||||
self.temperature = -273.15
|
||||
self.humidity = -1.0
|
||||
self.is_valid = False
|
||||
|
||||
|
||||
class EnvDataCollector(threading.Thread):
|
||||
def __init__(self, name, dht_pin, update_interval=10):
|
||||
super(EnvDataCollector, self).__init__()
|
||||
self.name = name
|
||||
self.sensor = 22
|
||||
self.dht_pin = dht_pin
|
||||
self.update_interval = update_interval
|
||||
self.run_condition = True
|
||||
self.data = EnvData()
|
||||
|
||||
def run(self):
|
||||
next_update = datetime.datetime.now()
|
||||
while self.run_condition:
|
||||
now = datetime.datetime.now()
|
||||
if now >= next_update:
|
||||
humidity, temperature = Adafruit_DHT.read(self.sensor, self.dht_pin)
|
||||
if humidity is None or temperature is None:
|
||||
log.info("%s: No data read - setting re-read intterval to 1s.", self.name)
|
||||
next_update = now + datetime.timedelta(seconds=1)
|
||||
else:
|
||||
if humidity >= 0.0 and humidity <= 100.0 and temperature >= -273.15:
|
||||
self.data.temperature = temperature
|
||||
self.data.humidity = humidity
|
||||
self.data.stamp = now
|
||||
self.data.is_valid = True
|
||||
next_update = now + datetime.timedelta(seconds=self.update_interval)
|
||||
log.info("%s: Data read - setting re-read intterval to %ss.",
|
||||
self.name, self.update_interval)
|
||||
else:
|
||||
log.info("%s: Invalid data read - setting re-read intterval to 1s.",
|
||||
self.name)
|
||||
next_update = now + datetime.timedelta(seconds=1)
|
||||
time.sleep(1)
|
||||
|
||||
def stop(self):
|
||||
self.run_condition = False
|
||||
self.join()
|
||||
|
||||
|
||||
class MqttHandler(object):
|
||||
def __init__(self, url, port, certificate):
|
||||
self.url = url
|
||||
self.port = port
|
||||
self.certificate = certificate
|
||||
self.client = mqtt.Client()
|
||||
self.client.tls_set(certificate)
|
||||
self.client.connect(url, port, 60)
|
||||
self.client.loop_start()
|
||||
|
||||
def stop(self):
|
||||
self.client.loop_stop()
|
||||
self.client.disconnect()
|
||||
|
||||
def transmit(self, topic, message):
|
||||
self.client.publish(topic, message, qos=2, retain=True)
|
||||
|
||||
|
||||
class Heat(object):
|
||||
def __init__(self, pin):
|
||||
self.pin = pin
|
||||
gpio.setwarnings(False)
|
||||
gpio.setmode(gpio.BCM)
|
||||
gpio.setup(pin, gpio.OUT)
|
||||
|
||||
def on(self):
|
||||
gpio.output(self.pin, 0)
|
||||
|
||||
def off(self):
|
||||
gpio.output(self.pin, 1)
|
||||
|
||||
|
||||
def main():
|
||||
old_green = EnvDataCollector("Greenhouse one", 17)
|
||||
old_heat = Heat(6)
|
||||
old_heat_state = "off"
|
||||
new_green = EnvDataCollector("Greenhouse two", 21)
|
||||
old_green.start()
|
||||
new_green.start()
|
||||
mqtt_handler = MqttHandler("mqtt.blackfinn.de", 8883, "/etc/ssl/certs/DST_Root_CA_X3.pem")
|
||||
try:
|
||||
next_update = datetime.datetime.now()
|
||||
while True:
|
||||
now = datetime.datetime.now()
|
||||
if now >= next_update:
|
||||
|
||||
# 1) Check timestamp of last data collection. If data are older
|
||||
# than 5 minutes the sensor isn't working anymore.
|
||||
if old_green.data.is_valid and old_green.data.stamp + datetime.timedelta(minutes=5) >= now:
|
||||
msg = "{} {}".format(datetime.datetime.timestamp(old_green.data.stamp), old_green.data.temperature)
|
||||
mqtt_handler.transmit("outdoor/greenhouse/temperature", msg)
|
||||
log.info("%s: Temperature: %s", old_green.data.stamp.time(), old_green.data.temperature)
|
||||
msg = "{} {}".format(datetime.datetime.timestamp(old_green.data.stamp), old_green.data.humidity)
|
||||
mqtt_handler.transmit("outdoor/greenhouse/humidity", msg)
|
||||
log.info("%s: Humidity: %s", old_green.data.stamp.time(), old_green.data.humidity)
|
||||
|
||||
if old_green.data.temperature < SWITCH_ON_TEMP and old_heat_state == "off":
|
||||
old_heat.on()
|
||||
old_heat_state = "on"
|
||||
msg = "{} {}".format(time.time(), old_heat_state)
|
||||
mqtt_handler.transmit("outdoor/greenhouse/heat", msg)
|
||||
log.info("%s: Heat: switch %s", time.time(), old_heat_state)
|
||||
elif old_green.data.temperature > SWITCH_OFF_TEMP and old_heat_state == "on":
|
||||
old_heat.off()
|
||||
old_heat_state = "off"
|
||||
msg = "{} {}".format(time.time(), old_heat_state)
|
||||
mqtt_handler.transmit("outdoor/greenhouse/heat", msg)
|
||||
log.info("%s: Heat: switch %s", time.time(), old_heat_state)
|
||||
next_update = now + datetime.timedelta(minutes=15)
|
||||
else:
|
||||
msg = "{} Error! Last valid DHT sensor data is from {}.".format(time.time(), old_green.data.stamp)
|
||||
mqtt_handler.transmit("outdoor/greenhouse/status", msg)
|
||||
log.error("%s: Error! Last valid DHT sensor data is from %s", time.time(), old_green.data.stamp)
|
||||
next_update = now + datetime.timedelta(seconds=5)
|
||||
time.sleep(1)
|
||||
except KeyboardInterrupt:
|
||||
log.info("Shutting down...")
|
||||
old_green.stop()
|
||||
new_green.stop()
|
||||
mqtt_handler.stop()
|
||||
log.info("Exiting...")
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
28
setup.py
Normal file
28
setup.py
Normal file
@ -0,0 +1,28 @@
|
||||
#!/usr/bin/python3
|
||||
'''
|
||||
@author: Thomas Klaehn <tkl@blackfinn.de>
|
||||
'''
|
||||
from distutils.core import setup
|
||||
import os
|
||||
import shutil
|
||||
import stat
|
||||
import sys
|
||||
|
||||
NAME = 'greenhouse'
|
||||
VERSION = '1.0.0'
|
||||
AUTHOR = 'Thomas Klaehn'
|
||||
EMAIL = 'tkl@blackfinn.de'
|
||||
SYSTEMD_SCRIPTS = ['greenhouse.service']
|
||||
PACKAGES = ['greenhouse']
|
||||
PACKAGE_DIRS = {'greenhouse':'greenhouse'}
|
||||
|
||||
SYSTEMD_PATH = '/lib/systemd/system/'
|
||||
|
||||
if sys.argv[1] == 'install':
|
||||
for script in SYSTEMD_SCRIPTS:
|
||||
shutil.copyfile(script, os.path.join(SYSTEMD_PATH, script))
|
||||
os.chmod(os.path.join(SYSTEMD_PATH, script), stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IROTH)
|
||||
setup(name=NAME, version=VERSION, author=AUTHOR, author_email=EMAIL, package_dir=PACKAGE_DIRS, packages=PACKAGES)
|
||||
elif sys.argv[1] == 'sdist':
|
||||
setup(name=NAME, version=VERSION, author=AUTHOR, author_email=EMAIL, package_dir=PACKAGE_DIRS, packages=PACKAGES,
|
||||
scripts=SYSTEMD_SCRIPTS)
|
Loading…
Reference in New Issue
Block a user