diff --git a/.gitlab-ci.yml b/._gitlab-ci.yml
similarity index 100%
rename from .gitlab-ci.yml
rename to ._gitlab-ci.yml
diff --git a/.project b/.project
deleted file mode 100644
index 127e014..0000000
--- a/.project
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
- chickenhouse
-
-
-
-
-
- org.python.pydev.PyDevBuilder
-
-
-
-
-
- org.python.pydev.pythonNature
-
-
diff --git a/.pydevproject b/.pydevproject
deleted file mode 100644
index 60cc573..0000000
--- a/.pydevproject
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
-/${PROJECT_DIR_NAME}/scripts
-/${PROJECT_DIR_NAME}/tests
-/${PROJECT_DIR_NAME}/gate_guard
-
-python 2.7
-Default
-
diff --git a/.pylintrc b/.pylintrc
deleted file mode 100644
index 1b59ef2..0000000
--- a/.pylintrc
+++ /dev/null
@@ -1,2 +0,0 @@
-[TYPECHECK]
-ignored-modules = numpy
\ No newline at end of file
diff --git a/gate_guard/__init__.py b/gate_guard/__init__.py
index d292206..4e6221b 100644
--- a/gate_guard/__init__.py
+++ b/gate_guard/__init__.py
@@ -9,7 +9,7 @@ import logging
import gate_guard.gate
def main():
- logging.basicConfig(filename='/var/log/gate_guard.log', level=logging.DEBUG)
+ 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:
diff --git a/gate_guard/engine.py b/gate_guard/engine.py
index 493fdf3..61cf2d2 100644
--- a/gate_guard/engine.py
+++ b/gate_guard/engine.py
@@ -17,13 +17,13 @@ class Engine(object):
self.stop()
def stop(self):
- self.gpio_1.set(0)
- self.gpio_2.set(0)
+ self.gpio_1.write(0)
+ self.gpio_2.write(0)
def up(self):
- self.gpio_1.set(1)
- self.gpio_2.set(0)
+ self.gpio_1.write(1)
+ self.gpio_2.write(0)
def down(self):
- self.gpio_1.set(0)
- self.gpio_2.set(1)
+ self.gpio_1.write(0)
+ self.gpio_2.write(1)
diff --git a/gate_guard/gate.py b/gate_guard/gate.py
index fc0ce70..b35bc21 100644
--- a/gate_guard/gate.py
+++ b/gate_guard/gate.py
@@ -3,28 +3,33 @@ Created on Dec 19, 2016
@author: klaehn
'''
+import datetime
+import logging
+import socket
+import ssl
import time
-import mqtt
+import paho.mqtt.client as mqtt
+import scipy.stats
import gate_guard.data_buffer
import gate_guard.light_sensor
import gate_guard.engine
import gate_guard.power_sensor
-import scipy
-import scipy.stats
-import logging
-STATE_INIT = "init"
+STATE_INIT_1 = "init_1"
+STATE_INIT_2 = "init_2"
+
STATE_OPENED = "open"
STATE_CLOSED = "close"
STATE_OPENING = "opening"
STATE_CLOSING = "closing"
-STATE_ERROR = "error"
LIGHT_READ_DELAY_S = 30
LIGHT_CONSECUTIVE_READS = 10
-LIGHT_LX_THRESHOLD = {"open":10, "close":5}
+LIGHT_LX_THRESHOLD = {"open":1, "close":0}
-MQTT_HOST = "proxy"
+MQTT_HOST = "mqtt.blackfinn.de"
+MQTT_PORT = 8883
+MQTT_CERTS = "/etc/ssl/certs/DST_Root_CA_X3.pem"
MQTT_TOPIC = "outdoor/chickenhouse/gate"
LIGHT_SENSOR_I2C_BUS = 1
@@ -33,50 +38,52 @@ LIGHT_SENSOR_I2C_ADDRESS = 0x23
POWER_SENSOR_I2C_BUS = 1
POWER_SENSOR_I2C_ADDRESS = 0x40
+POWER_CONSECUTIVE_READS = 10
+
SLOPE_COUNT = 10
SLOPE_CNT_MIN = 2
-
-MAX_GATE_RUNTIME = {"open":70, "close":60}
-MAX_POWER_SLOPE = {"up":40, "down":15}
+MAX_POWER = 400.0
class Gate(object):
def __init__(self):
- self.__state_handler = {STATE_INIT:self.__init_handler, \
+ self.__state_handler = {STATE_INIT_1:self.__init1_handler, \
+ STATE_INIT_2:self.__init2_handler, \
STATE_OPENED:self.__opened_handler, \
STATE_CLOSED:self.__closed_handler, \
STATE_OPENING:self.__opening_handler, \
- STATE_CLOSING:self.__closing_handler, \
- STATE_ERROR:self.__error_handler}
- self.__next_state = STATE_INIT
- self.__last_state = STATE_ERROR
-
+ STATE_CLOSING:self.__closing_handler}
+ self.__next_state = STATE_INIT_1
+ self.__last_state = STATE_OPENED
self.__light_sensor = gate_guard.light_sensor.LightSensor(
LIGHT_SENSOR_I2C_BUS, LIGHT_SENSOR_I2C_ADDRESS)
self.__light_data = gate_guard.data_buffer.DataBuffer(
LIGHT_CONSECUTIVE_READS)
- self.__comserver = mqtt.Mqtt(MQTT_HOST)
self.__engine = gate_guard.engine.Engine(gpio_1=13, gpio_2=19)
self.__power_sensor = gate_guard.power_sensor.PowerSensor(
POWER_SENSOR_I2C_BUS, POWER_SENSOR_I2C_ADDRESS)
- self.__gate_move_timeout = 0
+ self.__power_data = gate_guard.data_buffer.DataBuffer(POWER_CONSECUTIVE_READS)
self.__light_read_timeout = 0
- self.__error_count = 0
-
- self.slope_power = gate_guard.data_buffer.DataBuffer(SLOPE_COUNT)
- self.slope_time = gate_guard.data_buffer.DataBuffer(SLOPE_COUNT)
+ self.__down_run_time = 0
+ self.__gate_run_time = 0
+ self.__client = mqtt.Client()
+ self.__client.tls_set(MQTT_CERTS)
def poll(self):
current_time = time.time()
if current_time >= self.__light_read_timeout:
self.__light_read_timeout = current_time + LIGHT_READ_DELAY_S
- self.__light_data.push(self.__light_sensor.read())
+ light_read = self.__light_sensor.read()
+ self.__light_data.push(light_read)
+ logging.info('light - abs: ' + str(light_read) + ', avg: ' + \
+ str(self.__light_data.average()))
+ power_read = self.__power_sensor.power_mw()
+ self.__power_data.push(power_read)
self.__state_handler[self.__next_state](self.__light_data.average())
def __update_state(self, new_state):
self.__last_state = self.__next_state
self.__next_state = new_state
-
def __is_transition(self):
if self.__last_state != self.__next_state:
logging.info('STATE: ' + self.__last_state + ' -> ' + \
@@ -84,80 +91,123 @@ class Gate(object):
return True
return False
+ def __init1_handler(self, _):
+ next_state = self.__next_state
+ if self.__is_transition():
+ self.__engine.down()
+ # workaround for high power after starting engine
+ time.sleep(1)
+ msg = str(time.time()) + " Initialization"
+ try:
+ self.__client.connect(MQTT_HOST, MQTT_PORT)
+ self.__client.loop_start()
+ self.__client.publish(MQTT_TOPIC, msg, qos=2, retain=True)
+ self.__client.loop_stop()
+ except (ValueError, TypeError, socket.error, ssl.CertificateError):
+ logging.info('unable to publish to mqtt')
+ pwr = self.__power_sensor.power_mw()
+ logging.info('pwr: ' + str(pwr) + ' mW')
+ if pwr > MAX_POWER:
+ next_state = STATE_INIT_2
+ self.__update_state(next_state)
- def __init_handler(self, light_avg):
- '''
- In init we don't know anything neither about gate state nor about
- light. So first we try to reach STATE_OPENED.
- '''
- #pylint: disable=unused-argument
- self.__comserver.connect()
- self.__comserver.transmit(MQTT_TOPIC, str(time.time()) + \
- " gate gard initiated")
- self.__comserver.disconnect()
- self.__update_state(STATE_OPENING)
+ def __init2_handler(self, _):
+ next_state = self.__next_state
+ if self.__is_transition():
+ self.__engine.up()
+ self.__down_run_time = time.time()
+ pwr = self.__power_sensor.power_mw()
+ logging.info('pwr: ' + str(pwr) + ' mW')
+ if pwr > MAX_POWER:
+ self.__down_run_time = (time.time() - self.__down_run_time) / 2
+ logging.info('calculated down time: ' + str(self.__down_run_time) + ' s')
+ if self.__down_run_time > 30:
+ next_state = STATE_CLOSING
+ else:
+ logging.info("That's not very relaistic. Another try...")
+ next_state = STATE_INIT_1
+ self.__update_state(next_state)
+
+ def __check_to_open(self, light_avg):
+ ret = False
+ if (light_avg != None) and (light_avg > LIGHT_LX_THRESHOLD["open"]):
+ ret = True
+ current_date = datetime.datetime.now()
+ if (current_date.hour <= 12) and (current_date.hour >= 8):
+ ret = True
+ return ret
+
+ def __check_to_close(self, light_avg):
+ ret = False
+ if (light_avg != None) and (light_avg <= LIGHT_LX_THRESHOLD["close"]):
+ current_date = datetime.datetime.now()
+ if (current_date.hour >= 16) and (current_date.minute >= 0):
+ ret = True
+ return ret
def __opened_handler(self, light_avg):
next_state = self.__next_state
if self.__is_transition():
self.__engine.down()
- time.sleep(0.5)
+ time.sleep(1)
self.__engine.stop()
- slope, _, _, _, _ = scipy.stats.linregress(self.slope_time.get(),
- self.slope_power.get())
- self.__comserver.transmit(MQTT_TOPIC, str(time.time()) + \
- " Opened " + \
- str(slope))
- self.slope_power.clear()
- self.slope_time.clear()
-
- if (light_avg != None) and (light_avg <= LIGHT_LX_THRESHOLD["close"]):
+ msg = str(time.time()) + " Opened"
+ try:
+ self.__client.connect(MQTT_HOST, MQTT_PORT)
+ self.__client.loop_start()
+ self.__client.publish(MQTT_TOPIC, msg, qos=2, retain=True)
+ self.__client.loop_stop()
+ except (ValueError, TypeError, socket.error, ssl.CertificateError):
+ logging.info('unable to publish to mqtt')
+ if self.__check_to_close(light_avg) is True:
next_state = STATE_CLOSING
-
self.__update_state(next_state)
-
def __closed_handler(self, light_avg):
next_state = self.__next_state
if self.__is_transition():
self.__engine.up()
time.sleep(0.5)
self.__engine.stop()
- slope, _, _, _, _ = scipy.stats.linregress(self.slope_time.get(),
- self.slope_power.get())
- self.__comserver.transmit(MQTT_TOPIC, str(time.time()) + \
- " Closed " + \
- str(slope))
- self.slope_power.clear()
- self.slope_time.clear()
-
- if (light_avg != None) and (light_avg > LIGHT_LX_THRESHOLD["open"]):
+ msg = str(time.time()) + " Closed"
+ try:
+ self.__client.connect(MQTT_HOST, MQTT_PORT)
+ self.__client.loop_start()
+ self.__client.publish(MQTT_TOPIC, msg, qos=2, retain=True)
+ self.__client.loop_stop()
+ except (ValueError, TypeError, socket.error, ssl.CertificateError):
+ logging.info('unable to publish to mqtt')
+ if self.__check_to_open(light_avg) is True:
next_state = STATE_OPENING
-
self.__update_state(next_state)
-
def __opening_handler(self, light_avg):
next_state = self.__next_state
if self.__is_transition():
self.__engine.up()
- self.__gate_move_timeout = time.time() + MAX_GATE_RUNTIME["open"]
- self.__comserver.transmit(MQTT_TOPIC, str(time.time()) + \
- " Opening " + str(light_avg) + " lx")
- tm = time.time()
- if tm > self.__gate_move_timeout:
- next_state = STATE_ERROR
- else:
- pwr = self.__power_sensor.power_mw()
- self.slope_power.push(pwr)
- self.slope_time.push(tm)
- slope = 0
- if self.slope_power.length() >= SLOPE_CNT_MIN:
- slope, _, _, _, _ = scipy.stats.linregress(
- self.slope_time.get(), self.slope_power.get())
- logging.debug('up: ' + str(tm) + ' ' + str(pwr) + ' ' + str(slope))
- if slope > MAX_POWER_SLOPE["up"]:
+ msg = str(time.time()) + " Opening " + str(light_avg) + " lx"
+ try:
+ self.__client.connect(MQTT_HOST, MQTT_PORT)
+ self.__client.loop_start()
+ self.__client.publish(MQTT_TOPIC, msg, qos=2, retain=True)
+ self.__client.loop_stop()
+ except (ValueError, TypeError, socket.error, ssl.CertificateError):
+ logging.info('unable to publish to mqtt')
+ self.__gate_run_time = time.time()
+
+ # workaround for high power after starting engine
+ time.sleep(1)
+
+ pwr = self.__power_sensor.power_mw()
+ logging.info('pwr - abs: ' + str(pwr) + ' mW\tavg: ' + str(self.__power_data.average()) + ' mW')
+ if pwr > MAX_POWER:
+ deviation = abs(time.time() - self.__gate_run_time - self.__down_run_time)
+ logging.info('runtime deviation: ' + str(deviation))
+ if deviation > (self.__down_run_time / 10):
+ logging.info('Deviation too big. Re-initializing...')
+ next_state = STATE_INIT_1
+ else:
next_state = STATE_OPENED
self.__update_state(next_state)
@@ -165,31 +215,27 @@ class Gate(object):
next_state = self.__next_state
if self.__is_transition():
self.__engine.down()
- self.__gate_move_timeout = time.time() + MAX_GATE_RUNTIME["close"]
- self.__comserver.transmit(MQTT_TOPIC, str(time.time()) + \
- " Closing " + str(light_avg) + " lx")
+ msg = str(time.time()) + " Closing " + str(light_avg) + " lx"
+ try:
+ self.__client.connect(MQTT_HOST, MQTT_PORT)
+ self.__client.loop_start()
+ self.__client.publish(MQTT_TOPIC, msg, qos=2, retain=True)
+ self.__client.loop_stop()
+ except (ValueError, TypeError, socket.error, ssl.CertificateError):
+ logging.info('unable to publish to mqtt')
+ self.__gate_run_time = time.time()
- tm = time.time()
- if tm > self.__gate_move_timeout:
- next_state = STATE_ERROR
- else:
- pwr = self.__power_sensor.power_mw()
- self.slope_power.push(pwr)
- self.slope_time.push(tm)
- slope = 0
- if self.slope_power.length() >= SLOPE_CNT_MIN:
- slope, _, _, _, _ = scipy.stats.linregress(
- self.slope_time.get(), self.slope_power.get())
- logging.debug('dw: ' + str(tm) + ' ' + str(pwr) + ' ' + str(slope))
- if slope > MAX_POWER_SLOPE["down"]:
- next_state = STATE_CLOSED
+ # workaround for high power after starting engine
+ time.sleep(1)
+
+ pwr = self.__power_sensor.power_mw()
+ logging.info('pwr: ' + str(pwr) + ' mW')
+ opening_time = time.time() - self.__gate_run_time
+ if opening_time > self.__down_run_time:
+ logging.info("actual running time bigger than calculated (" + str(opening_time) + " vs. " + str(self.__down_run_time) + ").")
+ next_state = STATE_CLOSED
+ if pwr > MAX_POWER:
+ self.__engine.stop()
+ next_state = STATE_INIT_1
self.__update_state(next_state)
-
- def __error_handler(self, light_avg):
- #pylint: disable=unused-argument
- if self.__is_transition():
- self.__engine.stop()
- self.__comserver.transmit(MQTT_TOPIC, str(time.time()) + \
- " Error handler!!!")
- self.__update_state(STATE_INIT)
diff --git a/scripts/create_release_script.py b/scripts/create_release_script.py
index 5f1d2c5..6924ce9 100755
--- a/scripts/create_release_script.py
+++ b/scripts/create_release_script.py
@@ -10,7 +10,7 @@ import sys
DAEMON_START_SCRIPT_SRC = 'gate_guard.service'
DAEMON_START_SCRIPT_DST = '/lib/systemd/system/' + DAEMON_START_SCRIPT_SRC
-def main(argv):
+def main(_):
project_version = ''
project_name = ''
project_namespace = ''
@@ -62,4 +62,3 @@ def main(argv):
if __name__ == "__main__":
sys.exit(main(sys.argv[1:]))
-
diff --git a/tests/unittests/test_data_buffer.py b/tests/unittests/test_data_buffer.py
index e1a7333..558db24 100644
--- a/tests/unittests/test_data_buffer.py
+++ b/tests/unittests/test_data_buffer.py
@@ -10,7 +10,6 @@ import gate_guard.data_buffer
class Test(unittest.TestCase):
-
def test_zero_space(self):
dut = gate_guard.data_buffer.DataBuffer(0)
result = dut.push(10)
@@ -26,5 +25,4 @@ class Test(unittest.TestCase):
str(buffer_size) + " res: " + str(result))
if __name__ == "__main__":
- #import sys;sys.argv = ['', 'Test.testName']
- unittest.main()
\ No newline at end of file
+ unittest.main()