diff --git a/.gitignore b/.gitignore index d7e83b7..bfbe1c2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +config.py + # ---> Python # Byte-compiled / optimized / DLL files __pycache__/ diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..8830cd0 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,12 @@ +[submodule "report"] + path = report + url = https://git.mount-mockery.de/pylib/report.git +[submodule "geo"] + path = geo + url = https://git.mount-mockery.de/pylib/geo.git +[submodule "state_machine"] + path = state_machine + url = https://git.mount-mockery.de/pylib/state_machine.git +[submodule "task"] + path = task + url = https://git.mount-mockery.de/pylib/task.git diff --git a/.project b/.project new file mode 100644 index 0000000..d620ad4 --- /dev/null +++ b/.project @@ -0,0 +1,17 @@ + + + leyk + + + + + + org.python.pydev.PyDevBuilder + + + + + + org.python.pydev.pythonNature + + diff --git a/.pydevproject b/.pydevproject new file mode 100644 index 0000000..aa7a29a --- /dev/null +++ b/.pydevproject @@ -0,0 +1,8 @@ + + + + /${PROJECT_DIR_NAME} + + python interpreter + Default + diff --git a/__install__.py b/__install__.py new file mode 100644 index 0000000..89f95f8 --- /dev/null +++ b/__install__.py @@ -0,0 +1,41 @@ +#!/bin/python +# +import os +import sys + +SERVICE_FILE = """ +[Unit] +Description=Leyk Service +After=network-online.target +Wants=network-online.target +[Service] +User=%(UID)d +Group=%(GID)d +ExecStart=%(MY_PATH)s/leyk.sh +Type=simple +[Install] +WantedBy=default.target +""" + + +def help(): + print("Usage: prog ") + +if __name__ == "__main__": + if len(sys.argv) == 4: + try: + uid = int(sys.argv[1]) + gid = int(sys.argv[2]) + except ValueError: + help() + else: + if os.path.isdir(sys.argv[3]): + with open(os.path.join(sys.argv[3], 'leyk.service'), "w") as fh: + fh.write(SERVICE_FILE % { + "MY_PATH": os.path.dirname(os.path.abspath(__file__)), + "UID": uid, + "GID": gid}) + else: + help() + else: + help() diff --git a/examples/config.py b/examples/config.py new file mode 100644 index 0000000..3bf6604 --- /dev/null +++ b/examples/config.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python +# -*- coding: UTF-8 -*- +import os +import report + +MQTT_USER = "user" +MQTT_PASS = "password" +MQTT_SERVER = "host" +MQTT_TOPIC = "leyk" + +# +# Logging +# +__BASEPATH__ = os.path.abspath(os.path.dirname(__file__)) +APP_NAME = "leyk" +LOGTARGET = 'stdout' # possible choices are: 'logfile' or 'stdout' +LOGLVL = 'DEBUG' + +LOGHOST = 'cutelog' +LOGPORT = 19996 + +formatter = report.SHORT_FMT diff --git a/geo b/geo new file mode 160000 index 0000000..11166bb --- /dev/null +++ b/geo @@ -0,0 +1 @@ +Subproject commit 11166bb27ad2335f7812fcb88c788397f5106751 diff --git a/leyk.py b/leyk.py new file mode 100644 index 0000000..0efd71d --- /dev/null +++ b/leyk.py @@ -0,0 +1,23 @@ +import config +import logging +import report +import socket +import subprocess +import time +import piface_function + +try: + from config import APP_NAME as ROOT_LOGGER_NAME +except ImportError: + ROOT_LOGGER_NAME = 'root' +logger = logging.getLogger(ROOT_LOGGER_NAME).getChild('main') + + +if __name__ == '__main__': + report.appLoggingConfigure(config.__BASEPATH__, config.LOGTARGET, ((config.APP_NAME, config.LOGLVL), ), fmt=config.formatter, host=config.LOGHOST, port=config.LOGPORT) + # + l = piface_function.leyk() + + while True: + time.sleep(30) + #l.publish_states() diff --git a/leyk.sh b/leyk.sh new file mode 100755 index 0000000..5890bdd --- /dev/null +++ b/leyk.sh @@ -0,0 +1,4 @@ +#!/bin/sh +# +BASEPATH=`dirname $0` +$BASEPATH/venv/bin/python $BASEPATH/leyk.py \ No newline at end of file diff --git a/piface_function.py b/piface_function.py new file mode 100644 index 0000000..f11d63c --- /dev/null +++ b/piface_function.py @@ -0,0 +1,590 @@ +#!/usr/bin/env python +# -*- coding: UTF-8 -*- + +try: + import pifacedigitalio +except ImportError: + pifacedigitalio = None +import config +import geo +import json +import logging +import paho.mqtt.client as mqtt +import state_machine +import task +import random +import socket +import time + +try: + from config import APP_NAME as ROOT_LOGGER_NAME +except ImportError: + ROOT_LOGGER_NAME = 'root' +logger = logging.getLogger(ROOT_LOGGER_NAME).getChild(__name__) + +geo_position = geo.gps.coordinate(lat=49.976596, lon=9.1481443) + + +class pi_face(object): + LOG_PREFIX = 'PiFace:' + + PF_OUT_NAMES = ['Output 0', # 0 + 'Output 1', # 1 + 'Output 2', # 2 + 'Ploenlein', # 3 + 'Bakery', # 4 + 'Mill', # 5 + 'Reese House', # 6 + 'Bake House'] # 7 + + def __init__(self, client): + self.__client__ = client + + if pifacedigitalio is not None: + pifacedigitalio.init() + pi = pifacedigitalio.PiFaceDigital() + self.__pf_outputs__ = pi.output_pins + self.__pf_output_states__ = 8 * [False] + # + self._reload = 0 + self._task_10ms = task.periodic(0.01 - 0.007, self.task_10ms) + # + for i in range(0, 8): + self.set_output(i, self.__pf_output_states__[i]) + + def set_output(self, index, state, tries=5): + tries = min(max(1, tries), 5) + try_txt = ['1st', '2nd', '3rd', '4th', '5th'] + state = state is True + if pifacedigitalio is not None: + cnt = 0 + while (cnt < tries) and (self.__pf_outputs__[index].value != state): + self.__pf_outputs__[index].value = 1 * state + time.sleep(0.1) + cnt += 1 + if self.__pf_outputs__[index].value != state: + logger.warning('%s Control of output[%d] (%s) after %s try not successfull!', self.LOG_PREFIX, index, self.PF_OUT_NAMES[index], try_txt[cnt - 1]) + else: + logger.info('%s Set output "%s" to %s.', self.LOG_PREFIX, self.PF_OUT_NAMES[index], repr(state)) + self.__pf_output_states__[index] = self.__pf_outputs__[index].value == 1 + else: + logger.info('%s Set virtual output "%s" to %s.', self.LOG_PREFIX, self.PF_OUT_NAMES[index], repr(state)) + self.__pf_output_states__[index] = state + + def get_output(self, index): + return self.__pf_output_states__[index] + + def get_ploenlein(self): + return self.get_output(3) + + def set_bakery(self, state): + self.set_output(4, state) + + def get_bakery(self): + return self.get_output(4) + + def set_mill(self, state): + self.set_output(5, state) + + def get_mill(self): + return self.get_output(5) + + def set_ploenlein(self, state): + self.set_output(3, state) + + def set_reese_house(self, state): + self.set_output(6, state) + + def get_reese_house(self): + return self.get_output(6) + + def set_bake_house(self, state): + if state is True: + self.set_output(7, state) + self._task_10ms.run() + elif state is False: + self._task_10ms.stop() + self.join() + time.sleep(0.1) + self.set_output(7, state) + + def get_bake_house(self): + return not self._task_10ms._stopped + + def task_10ms(self, task_inst): + if self._reload <= 0: + if pifacedigitalio is not None: + self.__pf_outputs__[7].turn_on() + self._reload = random.choice(2 * [0] + 20 * [1] + 1 * [2]) + else: + if pifacedigitalio is not None: + self.__pf_outputs__[7].turn_off() + self._reload -= 1 + + def join(self): + self._task_10ms.join() + + def stop(self): + self._task_10ms.stop() + self._task_10ms.join() + for i in range(0, 8): + self.set_output(i, False) + pifacedigitalio.deinit() + + def __del__(self): + self.stop() + + +class state_machine_mode(state_machine.state_machine): + LOG_PREFIX = 'LeykMode:' + + STATE_AUTOMATIC = 'automatic' + STATE_MANUAL = 'manual' + + CONDITION_EXTERNAL_TRIGGER = 'external_trigger' + + TRANSITIONS = { + STATE_AUTOMATIC: ( + (CONDITION_EXTERNAL_TRIGGER, 1, STATE_MANUAL), + ), + STATE_MANUAL: ( + (CONDITION_EXTERNAL_TRIGGER, 1, STATE_AUTOMATIC), + ), + } + + def __init__(self, **kwargs): + state_machine.state_machine.__init__(self, self.STATE_AUTOMATIC, logging.INFO) + self.__reset_triggers__() + + def __reset_triggers__(self): + self.__to_automatic__ = False + self.__to_manual__ = False + + def external_trigger(self): + rv = False + if self.this_state() == self.STATE_AUTOMATIC: + rv = self.__to_manual__ + elif self.this_state() == self.STATE_MANUAL: + rv = self.__to_automatic__ + self.__reset_triggers__() + return rv + + def trigger_to_maual(self): + if self.this_state() == self.STATE_AUTOMATIC: + self.__to_manual__ = True + return True + return False + + def trigger_to_automatic(self): + if self.this_state() == self.STATE_MANUAL: + self.__to_automatic__ = True + return True + return False + + +class state_machine_day_state(state_machine.state_machine): + LOG_PREFIX = 'LeykState:' + + STATE_IDLE = 'idle' + STATE_WAKE = 'wake' + STATE_SUNRISE = 'sunrise' + STATE_SUNSET = 'sunset' + STATE_SLEEP = 'sleep' + + CONDITION_WAKE = 'condition_wake' + CONDITION_SUNRISE = 'condition_sunrise' + CONDITION_SUNSET = 'condition_sunset' + CONDITION_SLEEP = 'condition_sleep' + CONDITION_IDLE = 'condition_idle' + + TRANSITIONS = { + STATE_IDLE: ( + (CONDITION_WAKE, 1, STATE_WAKE), + (CONDITION_SUNRISE, 1, STATE_SUNRISE), + (CONDITION_SUNSET, 1, STATE_SUNSET), + (CONDITION_SLEEP, 1, STATE_SLEEP), + ), + STATE_WAKE: ( + (CONDITION_SUNRISE, 1, STATE_SUNRISE), + (CONDITION_IDLE, 1, STATE_IDLE), + ), + STATE_SUNRISE: ( + (CONDITION_SLEEP, 1, STATE_SLEEP), + (CONDITION_SUNSET, 1, STATE_SUNSET), + (CONDITION_IDLE, 1, STATE_IDLE), + ), + STATE_SUNSET: ( + (CONDITION_SLEEP, 1, STATE_SLEEP), + (CONDITION_IDLE, 1, STATE_IDLE), + ), + STATE_SLEEP: ( + (CONDITION_WAKE, 1, STATE_WAKE), + (CONDITION_SUNRISE, 1, STATE_SUNRISE), + (CONDITION_IDLE, 1, STATE_IDLE), + ), + } + + def __init__(self, **kwargs): + state_machine.state_machine.__init__(self, self.STATE_IDLE, logging.INFO, **kwargs) + + def __current_state_calc__(self): + def wake_time(): + tm = time.localtime() + tm = list(tm) + tm[3] = 6 # tm_hour + tm[4] = 0 # tm_min + tm[5] = 0 # tm_sec=0 + return time.mktime(time.struct_time(tm)) + + def sunrise_time(): + return time.mktime(geo.sun.sunrise(geo_position)) + 30 * 60 + + def sunset_time(): + return time.mktime(geo.sun.sunset(geo_position)) - 30 * 60 + + def sleep_time(): + tm = time.localtime() + tm = list(tm) + tm[3] = 21 # tm_hour + tm[4] = 15 # tm_min + tm[5] = 0 # tm_sec=0 + return time.mktime(time.struct_time(tm)) + + now = time.mktime(time.localtime()) + if now > sleep_time(): + return self.STATE_SLEEP + elif now > sunset_time(): + return self.STATE_SUNSET + elif now > sunrise_time(): + return self.STATE_SUNRISE + elif now > wake_time(): + return self.STATE_WAKE + else: + return self.STATE_IDLE + + def condition_wake(self): + if self.condition_idle(): + return False + return self.__current_state_calc__() == self.STATE_WAKE + + def condition_sunrise(self): + if self.condition_idle(): + return False + return self.__current_state_calc__() == self.STATE_SUNRISE + + def condition_sunset(self): + if self.condition_idle(): + return False + return self.__current_state_calc__() == self.STATE_SUNSET + + def condition_sleep(self): + if self.condition_idle(): + return False + return self.__current_state_calc__() == self.STATE_SLEEP + + def condition_idle(self): + return not self.sm_mode.this_state_is(self.sm_mode.STATE_AUTOMATIC) + + def report_sunset_sunrise(self): + state_machine.logger.debug('Sunrise: %s - Sunset: %s;', time.strftime("%H:%M", geo.sun.sunrise(geo_position)), time.strftime("%H:%M", geo.sun.sunset(geo_position)) +) + + +class leyk(object): + LOG_PREFIX = 'Leyk:' + + TOPIC_BAKE_HOUSE = config.MQTT_TOPIC + '/status/Bake House' + TOPIC_BAKERY = config.MQTT_TOPIC + '/status/Bakery' + TOPIC_MILL = config.MQTT_TOPIC + '/status/Mill' + TOPIC_MODE = config.MQTT_TOPIC + '/status/mode' + TOPIC_MODE_BOOL = config.MQTT_TOPIC + '/status/mode_bool' + TOPIC_PLOENLEIN = config.MQTT_TOPIC + '/status/Ploenlein' + TOPIC_REESE_HOUSE = config.MQTT_TOPIC + '/status/Reese House' + TOPIC_STATE = config.MQTT_TOPIC + '/status/state' + + + RX_TOPIC_BAKE_HOUSE = config.MQTT_TOPIC + '/set/Bake House' + RX_TOPIC_BAKERY = config.MQTT_TOPIC + '/set/Bakery' + RX_TOPIC_MILL = config.MQTT_TOPIC + '/set/Mill' + RX_TOPIC_MODE = config.MQTT_TOPIC + '/set/mode' + RX_TOPIC_PLOENLEIN = config.MQTT_TOPIC + '/set/Ploenlein' + RX_TOPIC_REESE_HOUSE = config.MQTT_TOPIC + '/set/Reese House' + + RX_TOPICS = [ + RX_TOPIC_BAKE_HOUSE, + RX_TOPIC_BAKERY, + RX_TOPIC_MILL, + RX_TOPIC_MODE, + RX_TOPIC_PLOENLEIN, + RX_TOPIC_REESE_HOUSE + ] + + def __init__(self): + self.__client__ = mqtt.Client("mqtt_leyk") # create client object + self.__client__.on_message = self.__receive__ # attach function to callback + self.__client__.username_pw_set(config.MQTT_USER, config.MQTT_PASS) # login with credentials + try: + self.__client__.connect(config.MQTT_SERVER, 1883) # establish connection + self.__client__.loop_start() # start the loop + self.__topics__ = [] + for topic in self.RX_TOPICS: + self.__client__.subscribe(topic) # subscibe a topic + except (socket.timeout, OSError) as e: + logger.warning("Error while setting up mqtt instance and listener") + + self.TOPIC_DATA = { + self.TOPIC_BAKE_HOUSE: self.get_bake_house, + self.TOPIC_BAKERY: self.get_bakery, + self.TOPIC_MILL: self.get_mill, + self.TOPIC_MODE: self.get_mode, + self.TOPIC_MODE_BOOL: self.get_mode_bool, + self.TOPIC_PLOENLEIN: self.get_ploenlein, + self.TOPIC_REESE_HOUSE: self.get_reese_house, + self.TOPIC_STATE: self.get_state + } + + self.sm_mode = state_machine_mode() + self.sm_day_state = state_machine_day_state(sm_mode=self.sm_mode) + self.__pf__ = pi_face(self.__client__) + self._queue = task.threaded_queue() + self._queue.run() + self._task_1s = task.periodic(1, self.task_1s) + self._task_1s.run() + self.sm_mode.register_state_change_callback(self.sm_mode.STATE_MANUAL, None, self._queue.clean_queue) + self.sm_mode.register_state_change_callback(None, None, self.publish, self.TOPIC_MODE) + self.sm_mode.register_state_change_callback(None, None, self.publish, self.TOPIC_MODE_BOOL) + self.sm_day_state.register_state_change_callback(None, None, self.clean_queue) + self.sm_day_state.register_state_change_callback(None, None, self.publish, self.TOPIC_STATE) + self.sm_day_state.register_state_change_callback(self.sm_day_state.STATE_SLEEP, None, self.sm_day_state.report_sunset_sunrise) + self.sm_day_state.register_state_change_callback(self.sm_day_state.STATE_SLEEP, None, self.fill_sleep_queue) + self.sm_day_state.register_state_change_callback(self.sm_day_state.STATE_SUNRISE, None, self.sm_day_state.report_sunset_sunrise) + self.sm_day_state.register_state_change_callback(self.sm_day_state.STATE_SUNRISE, None, self.fill_sunrise_queue) + self.sm_day_state.register_state_change_callback(self.sm_day_state.STATE_SUNSET, None, self.sm_day_state.report_sunset_sunrise) + self.sm_day_state.register_state_change_callback(self.sm_day_state.STATE_SUNSET, None, self.fill_sunset_queue) + self.sm_day_state.register_state_change_callback(self.sm_day_state.STATE_WAKE, None, self.sm_day_state.report_sunset_sunrise) + self.sm_day_state.register_state_change_callback(self.sm_day_state.STATE_WAKE, None, self.fill_wake_queue) + + self.publish(self.TOPIC_MODE) + self.publish(self.TOPIC_MODE_BOOL) + self.publish(self.TOPIC_STATE) + + def __receive__(self, client, userdata, message): + logger.info("Received message %s with %s", message.topic, str(message.payload)) + if message.topic == self.RX_TOPIC_MODE: + self.set_mode(self.sm_mode.STATE_AUTOMATIC if message.payload == b'true' else self.sm_mode.STATE_MANUAL) + elif message.topic == self.RX_TOPIC_BAKE_HOUSE: + self.set_bake_house(message.payload == b'true') + elif message.topic == self.RX_TOPIC_BAKERY: + self.set_bakery(message.payload == b'true') + elif message.topic == self.RX_TOPIC_MILL: + self.set_mill(message.payload == b'true') + elif message.topic == self.RX_TOPIC_PLOENLEIN: + self.set_ploenlein(message.payload == b'true') + elif message.topic == self.RX_TOPIC_REESE_HOUSE: + self.set_reese_house(message.payload == b'true') + else: + logger.warning("Ignoring unknown mqtt topic %s", message.topic) + + def publish_states(self): + for name in self.TOPIC_DATA: + self.publish(name) + + def publish(self, topic): + logger.info("Sending Leyk status information to mqtt %s = %s", topic, json.dumps(self.TOPIC_DATA[topic]())) + try: + self.__client__.publish(topic, json.dumps(self.TOPIC_DATA[topic]())) + except (socket.timeout, OSError) as e: + logger.warning("Error while sending state information information") + + def set_mode(self, mode): + if mode == self.sm_mode.STATE_AUTOMATIC: + rv = self.sm_mode.trigger_to_automatic() + elif mode == self.sm_mode.STATE_MANUAL: + rv = self.sm_mode.trigger_to_maual() + else: + rv = False + return rv + + def get_mode(self): + return self.sm_mode.this_state() + + def get_mode_bool(self): + return self.get_mode() == self.sm_mode.STATE_AUTOMATIC + + def get_state(self): + return self.sm_day_state.this_state() + + def __queue_wrapper__(self, queue_inst, function, *args, **kwargs): + function(*args, **kwargs) + + def set_ploenlein(self, state, force=False): + if force or self.sm_mode.this_state_is(self.sm_mode.STATE_MANUAL): + self.__pf__.set_ploenlein(state) + self.publish(self.TOPIC_PLOENLEIN) + return True + else: + return False + + def get_ploenlein(self): + return self.__pf__.get_ploenlein() + + def set_bakery(self, state, force=False): + if force or self.sm_mode.this_state_is(self.sm_mode.STATE_MANUAL): + self.__pf__.set_bakery(state) + self.publish(self.TOPIC_BAKERY) + return True + else: + return False + + def get_bakery(self): + return self.__pf__.get_bakery() + + def set_bake_house(self, state, force=False): + if force or self.sm_mode.this_state_is(self.sm_mode.STATE_MANUAL): + self.__pf__.set_bake_house(state) + self.publish(self.TOPIC_BAKE_HOUSE) + return True + else: + return False + + def get_bake_house(self): + return self.__pf__.get_bake_house() + + def set_mill(self, state, force=False): + if force or self.sm_mode.this_state_is(self.sm_mode.STATE_MANUAL): + self.__pf__.set_mill(state) + self.publish(self.TOPIC_MILL) + return True + else: + return False + + def get_mill(self): + return self.__pf__.get_mill() + + def set_reese_house(self, state, force=False): + if force or self.sm_mode.this_state_is(self.sm_mode.STATE_MANUAL): + self.__pf__.set_reese_house(state) + self.publish(self.TOPIC_REESE_HOUSE) + return True + else: + return False + + def get_reese_house(self): + return self.__pf__.get_reese_house() + + def wake_time(self): + tm = time.localtime() + tm = list(tm) + tm[3] = 6 # tm_hour + tm[4] = 0 # tm_min + tm[5] = 0 # tm_sec=0 + return time.mktime(time.struct_time(tm)) + + def sunrise_time(self): + return time.mktime(geo.sun.sunrise(geo_position)) + 30 * 60 + + def sunset_time(self): + return time.mktime(geo.sun.sunset(geo_position)) - 30 * 60 + + def sleep_time(self): + tm = time.localtime() + tm = list(tm) + tm[3] = 21 # tm_hour + tm[4] = 15 # tm_min + tm[5] = 0 # tm_sec=0 + return time.mktime(time.struct_time(tm)) + + def identify_current_state(self): + now = time.mktime(time.localtime()) + if now > self.sleep_time(): + return self.ST_SLEEP + elif now > self.sunset_time(): + return self.ST_SUNSET + elif now > self.sunrise_time(): + return self.ST_SUNRISE + elif now > self.wake_time(): + return self.ST_WAKE + else: + return self.ST_SLEEP + + def wait(self, queue_inst, delay): + if delay > 0: + logger.debug('%s Wait for %d seconds initiated. %d elements left in queue.', self.LOG_PREFIX, delay, queue_inst.qsize()) + cnt = 0 + while cnt < delay * 5: + if queue_inst.qsize() == 0: + logger.debug('%s Quit wait for %d seconds. %d elements left in queue.', self.LOG_PREFIX, delay, queue_inst.qsize()) + break + time.sleep(0.2) + cnt += 1 + + def clean_queue(self): + if self._queue.qsize() > 0: + logger.info('Cleaning up remaining %d elements from queue', self._queue.qsize()) + self._queue.clean_queue() + + def fill_wake_queue(self): + no_delay = self.sm_day_state.previous_state_was(self.sm_day_state.STATE_IDLE) + # WAKE + self._queue.enqueue(1, self.__queue_wrapper__, self.set_bakery, True, True) + self._queue.enqueue(2, self.wait, 0 if no_delay else 5 * 60) + self._queue.enqueue(3, self.__queue_wrapper__, self.set_bake_house, True, True) + self._queue.enqueue(4, self.wait, 0 if no_delay else 10 * 60) + self._queue.enqueue(5, self.__queue_wrapper__, self.set_reese_house, True, True) + self._queue.enqueue(6, self.wait, 0 if no_delay else 7 * 60) + self._queue.enqueue(7, self.__queue_wrapper__, self.set_ploenlein, True, True) + self._queue.enqueue(8, self.wait, 0 if no_delay else 8 * 60) + self._queue.enqueue(9, self.__queue_wrapper__, self.set_mill, True, True) + + def fill_sunrise_queue(self): + no_delay = self.sm_day_state.previous_state_was(self.sm_day_state.STATE_IDLE) + # SUNRISE + self._queue.enqueue(1, self.__queue_wrapper__, self.set_bake_house, True, True) + self._queue.enqueue(2, self.__queue_wrapper__, self.set_ploenlein, False, True) + self._queue.enqueue(3, self.wait, 0 if no_delay else 5 * 60) + self._queue.enqueue(4, self.__queue_wrapper__, self.set_mill, False, True) + self._queue.enqueue(5, self.wait, 0 if no_delay else 8 * 60) + self._queue.enqueue(6, self.__queue_wrapper__, self.set_bakery, False, True) + self._queue.enqueue(7, self.wait, 0 if no_delay else 12 * 60) + self._queue.enqueue(8, self.__queue_wrapper__, self.set_reese_house, False, True) + + def fill_sunset_queue(self): + no_delay = self.sm_day_state.previous_state_was(self.sm_day_state.STATE_IDLE) + # SUNSET + self._queue.enqueue(1, self.__queue_wrapper__, self.set_bake_house, True, True) + self._queue.enqueue(2, self.__queue_wrapper__, self.set_bakery, True, True) + self._queue.enqueue(3, self.wait, 0 if no_delay else 10 * 60) + self._queue.enqueue(4, self.__queue_wrapper__, self.set_reese_house, True, True) + self._queue.enqueue(5, self.wait, 0 if no_delay else 8 * 60) + self._queue.enqueue(6, self.__queue_wrapper__, self.set_mill, True, True) + self._queue.enqueue(7, self.wait, 0 if no_delay else 7 * 60) + self._queue.enqueue(8, self.__queue_wrapper__, self.set_ploenlein, True, True) + + def fill_sleep_queue(self): + no_delay = self.sm_day_state.previous_state_was(self.sm_day_state.STATE_IDLE) + # SLEEP + self._queue.enqueue(1, self.__queue_wrapper__, self.set_bake_house, False, True) + self._queue.enqueue(2, self.wait, 0 if no_delay else 5 * 60) + self._queue.enqueue(3, self.__queue_wrapper__, self.set_bakery, False, True) + self._queue.enqueue(4, self.wait, 0 if no_delay else 9 * 60) + self._queue.enqueue(5, self.__queue_wrapper__, self.set_ploenlein, False, True) + self._queue.enqueue(6, self.wait, 0 if no_delay else 9 * 60) + self._queue.enqueue(7, self.__queue_wrapper__, self.set_mill, False, True) + self._queue.enqueue(8, self.wait, 0 if no_delay else 6 * 60) + self._queue.enqueue(9, self.__queue_wrapper__, self.set_reese_house, False, True) + + def task_1s(self, task_inst): + self.sm_mode.work() + self.sm_day_state.work() + + def join(self): + self._task_1s.join() + self._queue.join() + + def stop(self): + self._task_1s.stop() + self._queue.stop() + + def __del__(self): + self.__client__.loop_stop() # stop the loop + self.stop() diff --git a/report b/report new file mode 160000 index 0000000..21bac82 --- /dev/null +++ b/report @@ -0,0 +1 @@ +Subproject commit 21bac82e0c459ebf6d34783c9249526a657a6bbd diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..58820cd --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +pifacecommon +pifacedigitalio +paho-mqtt diff --git a/state_machine b/state_machine new file mode 160000 index 0000000..c47c20e --- /dev/null +++ b/state_machine @@ -0,0 +1 @@ +Subproject commit c47c20e0d69cdefe498c38e459ec8f4accb642bd diff --git a/task b/task new file mode 160000 index 0000000..7583bb5 --- /dev/null +++ b/task @@ -0,0 +1 @@ +Subproject commit 7583bb5f3bd2420c901374ba95b678af6ce88433