From c3fe5d4591ebd186b4a91865aba12855d323f00e Mon Sep 17 00:00:00 2001 From: Eric Davisson Date: Tue, 11 Aug 2020 13:14:45 -0500 Subject: [PATCH 01/97] Adding Support for BME680 Adding Support for Pressure Sensor BME680 over I2C --- LICENSE | 21 -------------- README.md | 2 +- package.json | 2 +- requirements.txt | 3 +- sensors/pi/bme680_sensor.py | 57 +++++++++++++++++++++++++++++++++++++ 5 files changed, 61 insertions(+), 24 deletions(-) delete mode 100644 LICENSE create mode 100644 sensors/pi/bme680_sensor.py diff --git a/LICENSE b/LICENSE deleted file mode 100644 index d7c8722..0000000 --- a/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2018 Ed Davisson - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/README.md b/README.md index 5cc357c..35f1509 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ These are the devices and sensors I tested and used with MudPi successfully. Man Let me know if you are able to confirm tests on any other devices ## License -This project is licensed under the MIT License - see the [LICENSE.md](LICENSE.md) file for details +This project is licensed under the BSD-4-Clause License - see the [LICENSE.md](LICENSE.md) file for details MudPi Smart Garden diff --git a/package.json b/package.json index 6f4dfd0..467a79e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "mudpi-core", - "version": "0.8.8", + "version": "0.8.11", "description": "Configurable automated smart garden for raspberry pi", "bugs": "https://github.com/mudpi/mudpi-core/issues", "contributors": [ diff --git a/requirements.txt b/requirements.txt index 222e642..c65c3ea 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,4 +4,5 @@ adafruit-circuitpython-mcp3xxx adafruit-blinka pycron redis -picamera \ No newline at end of file +picamera +adafruit-circuitpython-bme680 \ No newline at end of file diff --git a/sensors/pi/bme680_sensor.py b/sensors/pi/bme680_sensor.py new file mode 100644 index 0000000..5f07abd --- /dev/null +++ b/sensors/pi/bme680_sensor.py @@ -0,0 +1,57 @@ +import time +import json +import redis +from .sensor import Sensor +import RPi.GPIO as GPIO +import board +from busio import I2C +import adafruit_bme680 + +import sys +sys.path.append('..') + +import variables + +#r = redis.Redis(host='127.0.0.1', port=6379) +#PIN MODE : OUT | IN + +class Bme680Sensor(Sensor): + + def __init__(self, pin, name='PressureSensor', key=None): + super().__init__(pin, name=name, key=key) + return + + def init_sensor(self): + #Initialize the sensor here (i.e. set pin mode, get addresses, etc) this gets called by the worker + self.i2c = I2C(board.SCL, board.SDA) + self.sensor = adafruit_bme680.Adafruit_BME680_I2C(i2c, debug=False) + # change this to match the location's pressure (hPa) at sea level + self.sensor.sea_level_pressure = 1013.25 + return + + def read(self): + #Read the sensor(s), parse the data and store it in redis if redis is configured + + temperature = round((self.sensor.temperature - 5) * 1.8 + 32, 2) + gas = self.sensor.gas + humidity = round(self.sensor.humidity, 1) + pressure = self.sensor.pressure + altitude = round(self.sensor.altitude, 3) + + if humidity is not None and temperature is not None: + variables.r.set(self.key + '_temperature', temperature) + variables.r.set(self.key + '_humidity', humidity) + variables.r.set(self.key + '_gas', gas) + variables.r.set(self.key + '_pressure', pressure) + variables.r.set(self.key + '_altitude', altitude) + readings = {'temperature': temperature, 'humidity': humidity, 'pressure': pressure, 'gas': gas, 'altitude': altitude} + variables.r.set(self.key, json.dumps(readings)) + print('BME680:', readings) + return readings + else: + print('Failed to get reading [BME680]. Try again!') + + + def readRaw(self): + #Read the sensor(s) but return the raw data, useful for debugging + return self.read() From d4e50af10163e7ccf7eb6b20659f90b37e363669 Mon Sep 17 00:00:00 2001 From: Eric Davisson Date: Tue, 11 Aug 2020 13:32:34 -0500 Subject: [PATCH 02/97] Added Null Values for BME680 --- sensors/pi/bme680_sensor.py | 2 +- workers/pi_sensor_worker.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sensors/pi/bme680_sensor.py b/sensors/pi/bme680_sensor.py index 5f07abd..f1b4f14 100644 --- a/sensors/pi/bme680_sensor.py +++ b/sensors/pi/bme680_sensor.py @@ -17,7 +17,7 @@ class Bme680Sensor(Sensor): - def __init__(self, pin, name='PressureSensor', key=None): + def __init__(self, pin = None, name='PressureSensor', key=None): super().__init__(pin, name=name, key=key) return diff --git a/workers/pi_sensor_worker.py b/workers/pi_sensor_worker.py index df2be6d..98abf34 100644 --- a/workers/pi_sensor_worker.py +++ b/workers/pi_sensor_worker.py @@ -47,7 +47,7 @@ def init_sensors(self): # Define default kwargs for all sensor types, conditionally include optional variables below if they exist sensor_kwargs = { 'name' : sensor.get('name', sensor.get('type')), - 'pin' : int(sensor.get('pin')), + 'pin' : int(sensor.get('pin', None)), 'key' : sensor.get('key', None) } From 38e489fdfc1c5b15d1c1822c6ee752ae1c9345ef Mon Sep 17 00:00:00 2001 From: Eric Davisson Date: Tue, 11 Aug 2020 13:50:44 -0500 Subject: [PATCH 03/97] Added Sensor Import --- workers/pi_sensor_worker.py | 1 + 1 file changed, 1 insertion(+) diff --git a/workers/pi_sensor_worker.py b/workers/pi_sensor_worker.py index 98abf34..067cfe7 100644 --- a/workers/pi_sensor_worker.py +++ b/workers/pi_sensor_worker.py @@ -7,6 +7,7 @@ sys.path.append('..') from sensors.pi.float_sensor import (FloatSensor) from sensors.pi.humidity_sensor import (HumiditySensor) +from sensors.pi.bme680_sensor import (Bme680Sensor) import variables From 38a7a3f7510a7e13760d66c2029f8100757bbd16 Mon Sep 17 00:00:00 2001 From: Eric Davisson Date: Tue, 11 Aug 2020 13:51:45 -0500 Subject: [PATCH 04/97] Fixed indentation --- sensors/pi/bme680_sensor.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sensors/pi/bme680_sensor.py b/sensors/pi/bme680_sensor.py index f1b4f14..1c8ea0f 100644 --- a/sensors/pi/bme680_sensor.py +++ b/sensors/pi/bme680_sensor.py @@ -33,10 +33,10 @@ def read(self): #Read the sensor(s), parse the data and store it in redis if redis is configured temperature = round((self.sensor.temperature - 5) * 1.8 + 32, 2) - gas = self.sensor.gas - humidity = round(self.sensor.humidity, 1) - pressure = self.sensor.pressure - altitude = round(self.sensor.altitude, 3) + gas = self.sensor.gas + humidity = round(self.sensor.humidity, 1) + pressure = self.sensor.pressure + altitude = round(self.sensor.altitude, 3) if humidity is not None and temperature is not None: variables.r.set(self.key + '_temperature', temperature) From f39052cb41b1d596cc595ad743a3f900e0f8ef27 Mon Sep 17 00:00:00 2001 From: Eric Davisson Date: Tue, 11 Aug 2020 13:52:58 -0500 Subject: [PATCH 05/97] Fixed null pins in pi worker --- workers/pi_sensor_worker.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/workers/pi_sensor_worker.py b/workers/pi_sensor_worker.py index 067cfe7..2d7c676 100644 --- a/workers/pi_sensor_worker.py +++ b/workers/pi_sensor_worker.py @@ -48,7 +48,7 @@ def init_sensors(self): # Define default kwargs for all sensor types, conditionally include optional variables below if they exist sensor_kwargs = { 'name' : sensor.get('name', sensor.get('type')), - 'pin' : int(sensor.get('pin', None)), + 'pin' : int(sensor.get('pin', 0)), 'key' : sensor.get('key', None) } From db5de91a81972401e0e07cd2ce3eb241f10a4b4e Mon Sep 17 00:00:00 2001 From: Eric Davisson Date: Tue, 11 Aug 2020 13:53:52 -0500 Subject: [PATCH 06/97] Fixed error in BME680 sensor --- sensors/pi/bme680_sensor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sensors/pi/bme680_sensor.py b/sensors/pi/bme680_sensor.py index 1c8ea0f..7989164 100644 --- a/sensors/pi/bme680_sensor.py +++ b/sensors/pi/bme680_sensor.py @@ -24,7 +24,7 @@ def __init__(self, pin = None, name='PressureSensor', key=None): def init_sensor(self): #Initialize the sensor here (i.e. set pin mode, get addresses, etc) this gets called by the worker self.i2c = I2C(board.SCL, board.SDA) - self.sensor = adafruit_bme680.Adafruit_BME680_I2C(i2c, debug=False) + self.sensor = adafruit_bme680.Adafruit_BME680_I2C(self.i2c, debug=False) # change this to match the location's pressure (hPa) at sea level self.sensor.sea_level_pressure = 1013.25 return From f173412af162a90ce5f4f7bd52d1bb3515e11785 Mon Sep 17 00:00:00 2001 From: Eric Davisson Date: Tue, 11 Aug 2020 14:01:28 -0500 Subject: [PATCH 07/97] Made BME680 values more friendly --- sensors/pi/bme680_sensor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sensors/pi/bme680_sensor.py b/sensors/pi/bme680_sensor.py index 7989164..c30d2ab 100644 --- a/sensors/pi/bme680_sensor.py +++ b/sensors/pi/bme680_sensor.py @@ -35,7 +35,7 @@ def read(self): temperature = round((self.sensor.temperature - 5) * 1.8 + 32, 2) gas = self.sensor.gas humidity = round(self.sensor.humidity, 1) - pressure = self.sensor.pressure + pressure = round(self.sensor.pressure, 2) altitude = round(self.sensor.altitude, 3) if humidity is not None and temperature is not None: From bd30ce3b9e4dec5369e6739aeefdc75bfeac56b4 Mon Sep 17 00:00:00 2001 From: Eric Davisson Date: Tue, 11 Aug 2020 14:04:55 -0500 Subject: [PATCH 08/97] Cleaned up Float Sensor --- sensors/arduino/float_sensor.py | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/sensors/arduino/float_sensor.py b/sensors/arduino/float_sensor.py index 6f98959..8f7df6c 100644 --- a/sensors/arduino/float_sensor.py +++ b/sensors/arduino/float_sensor.py @@ -12,7 +12,6 @@ default_connection = SerialManager(device='/dev/ttyUSB0') #r = redis.Redis(host='127.0.0.1', port=6379) - class FloatSensor(Sensor): def __init__(self, pin, name='FloatSensor', key=None, connection=default_connection): @@ -29,19 +28,4 @@ def read(self): return value def readRaw(self): - return self.read() - - -if __name__ == '__main__': - try: - loop_count = 10 - while (loop_count > 0): - sensor = FloatSensor(9) - rainread = sensor.read() - print('Float: ', rainread) - loop_count += 1 - time.sleep(3) - except KeyboardInterrupt: - pass - finally: - print('Float Sensor Closing...') \ No newline at end of file + return self.read() \ No newline at end of file From c3efae630eecbac370076a33509df977382450d9 Mon Sep 17 00:00:00 2001 From: Eric Davisson Date: Tue, 11 Aug 2020 14:05:46 -0500 Subject: [PATCH 09/97] Added Support for Ambient Light Module Cleaned up Light sensor to work with complete ambient light module. (Essentially an analog read) --- sensors/arduino/light_sensor.py | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/sensors/arduino/light_sensor.py b/sensors/arduino/light_sensor.py index 79c260c..7bfd19a 100644 --- a/sensors/arduino/light_sensor.py +++ b/sensors/arduino/light_sensor.py @@ -4,9 +4,13 @@ import redis from .sensor import Sensor from nanpy import (ArduinoApi, SerialManager) +import sys +sys.path.append('..') + +import variables default_connection = SerialManager(device='/dev/ttyUSB0') -r = redis.Redis(host='127.0.0.1', port=6379) +#r = redis.Redis(host='127.0.0.1', port=6379) class LightSensor(Sensor): @@ -19,19 +23,9 @@ def init_sensor(self): self.api.pinMode(self.pin, self.api.INPUT) def read(self): - ldr_resistance = self.api.analogRead(self.pin) - resistor1 = 10 #1k Resistor in the divider - - Vout = ldr_resistance * 0.0048828125 #Some frequency clock thing related to amps - #lux = 500 / ( resistor1 * ( (5 - Vout) / Vout )) #calculate lux using voltage divider formula with LDR to lux conversion - lux = ( 2500 / Vout - 500 ) / resistor1 - - #print("ldr_resistance: %d" % ldr_resistance) - r.set(self.key, lux) - return lux + light_intesity = self.api.analogRead(self.pin) + r.set(self.key, light_intesity) + return light_intesity def readRaw(self): - resistance = self.api.analogRead(self.pin) - #print("Resistance: %d" % resistance) - r.set(self.key+'_raw', resistance) - return resistance \ No newline at end of file + return self.read() \ No newline at end of file From 7da24a52279b8fb4ea9b98963a993f3fe3c35982 Mon Sep 17 00:00:00 2001 From: Eric Davisson Date: Tue, 11 Aug 2020 14:22:08 -0500 Subject: [PATCH 10/97] Added catch for bad sensor models --- workers/pi_sensor_worker.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/workers/pi_sensor_worker.py b/workers/pi_sensor_worker.py index 2d7c676..4d1bd33 100644 --- a/workers/pi_sensor_worker.py +++ b/workers/pi_sensor_worker.py @@ -55,7 +55,7 @@ def init_sensors(self): # optional sensor variables # Model is specific to DHT modules to specify DHT11 DHT22 or DHT2302 if sensor.get('model'): - sensor_kwargs['model'] = sensor.get('model') + sensor_kwargs['model'] = str(sensor.get('model')) new_sensor = imported_sensor(**sensor_kwargs) new_sensor.init_sensor() From 13b99546854aff8729365f5823cc12f376c2b206 Mon Sep 17 00:00:00 2001 From: Eric Davisson Date: Tue, 11 Aug 2020 14:34:35 -0500 Subject: [PATCH 11/97] Fixed server being required --- mudpi.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/mudpi.py b/mudpi.py index 143e7ab..48e83d9 100755 --- a/mudpi.py +++ b/mudpi.py @@ -211,12 +211,14 @@ #Maybe use this for internal communication across devices if using wireless def server_worker(): server.listen() - print('MudPi Server...\t\t\t\t\033[1;33m Starting\033[0;0m', end='\r', flush=True) - time.sleep(1) - server = MudpiServer(main_thread_running, CONFIGS['server']['host'], CONFIGS['server']['port']) - s = threading.Thread(target=server_worker) - threads.append(s) - s.start() + + if (CONFIGS['server'] is not None): + print('MudPi Server...\t\t\t\t\033[1;33m Starting\033[0;0m', end='\r', flush=True) + time.sleep(1) + server = MudpiServer(main_thread_running, CONFIGS['server']['host'], CONFIGS['server']['port']) + s = threading.Thread(target=server_worker) + threads.append(s) + s.start() time.sleep(.5) From 83f39801851b66309ba5c22ff657074c2f19630d Mon Sep 17 00:00:00 2001 From: Eric Davisson Date: Tue, 11 Aug 2020 14:36:15 -0500 Subject: [PATCH 12/97] Added error catching for Server --- mudpi.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/mudpi.py b/mudpi.py index 48e83d9..ccec210 100755 --- a/mudpi.py +++ b/mudpi.py @@ -212,13 +212,17 @@ def server_worker(): server.listen() - if (CONFIGS['server'] is not None): - print('MudPi Server...\t\t\t\t\033[1;33m Starting\033[0;0m', end='\r', flush=True) - time.sleep(1) - server = MudpiServer(main_thread_running, CONFIGS['server']['host'], CONFIGS['server']['port']) - s = threading.Thread(target=server_worker) - threads.append(s) - s.start() + try: + if (CONFIGS['server'] is not None): + print('MudPi Server...\t\t\t\t\033[1;33m Starting\033[0;0m', end='\r', flush=True) + time.sleep(1) + server = MudpiServer(main_thread_running, CONFIGS['server']['host'], CONFIGS['server']['port']) + s = threading.Thread(target=server_worker) + threads.append(s) + s.start() + except KeyError: + print('No Server Config Found to Load') + traceback.print_exc() time.sleep(.5) From 0a9b4b8cf4e51cfff820c1966249fd7961aa237d Mon Sep 17 00:00:00 2001 From: Eric Davisson Date: Tue, 11 Aug 2020 14:39:53 -0500 Subject: [PATCH 13/97] Fixed server shutdown --- mudpi.py | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/mudpi.py b/mudpi.py index ccec210..f22f581 100755 --- a/mudpi.py +++ b/mudpi.py @@ -5,7 +5,6 @@ import time import json import sys -import traceback sys.path.append('..') from action import Action from config_load import loadConfigJson @@ -136,7 +135,6 @@ threads.append(pw) except KeyError: print('No Pi Workers Found to Load or Invalid Type') - traceback.print_exc() # Worker for relays attached to pi @@ -159,7 +157,6 @@ threads.append(r) except KeyError: print('No Relays Found to Load') - traceback.print_exc() # Worker for nodes attached to pi via serial or wifi[esp8266] # Supported nodes: arduinos, esp8266, ADC-MCP3xxx, probably others @@ -183,7 +180,6 @@ threads.append(t) except KeyError as e: print('Invalid or no Nodes found to Load') - traceback.print_exc() # Load in Actions @@ -194,7 +190,6 @@ actions[a.key] = a except KeyError: print('No Actions Found to Load') - traceback.print_exc() # Worker for Triggers try: @@ -204,7 +199,6 @@ threads.append(t) except KeyError: print('No Triggers Found to Load') - traceback.print_exc() #Decided not to build server worker (this is replaced with nodejs, expressjs) @@ -222,7 +216,6 @@ def server_worker(): s.start() except KeyError: print('No Server Config Found to Load') - traceback.print_exc() time.sleep(.5) @@ -250,7 +243,11 @@ def server_worker(): #load a client on the server to clear it from waiting # sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) #sock.connect((CONFIGS['SERVER_HOST'], int(CONFIGS['SERVER_PORT']))) - server.sock.shutdown(socket.SHUT_RDWR) + # + try: + server.sock.shutdown(socket.SHUT_RDWR) + except: + pass # time.sleep(1) # sock.close() From 4be2807e7859c435c06ade49003495093ca89e55 Mon Sep 17 00:00:00 2001 From: Eric Davisson Date: Tue, 11 Aug 2020 15:59:15 -0500 Subject: [PATCH 14/97] Adding Support for Node Relays --- mudpi.py | 6 -- workers/arduino_relay_worker.py | 167 ++++++++++++++++++++++++++++++++ workers/arduino_worker.py | 55 ++++++++--- 3 files changed, 210 insertions(+), 18 deletions(-) create mode 100644 workers/arduino_relay_worker.py diff --git a/mudpi.py b/mudpi.py index f22f581..602cd42 100755 --- a/mudpi.py +++ b/mudpi.py @@ -89,7 +89,6 @@ relays = [] relayEvents = {} relay_index = 0 - variables.lcd_message = {'line_1': 'Mudpi Control', 'line_2': 'Is Now Running'} new_messages_waiting = threading.Event() #Event to signal LCD to pull new messages main_thread_running = threading.Event() #Event to signal workers to close @@ -100,11 +99,6 @@ time.sleep(0.1) print('Preparing Threads for Workers...\t\033[1;32m Complete\033[0;0m') - #l = LCDWorker(new_messages_waiting,main_thread_running,system_ready) - #print('Loading LCD Worker') - #l = l.run() - #threads.append(l) - # Worker for Camera try: c = CameraWorker(CONFIGS['camera'], main_thread_running, system_ready, camera_available) diff --git a/workers/arduino_relay_worker.py b/workers/arduino_relay_worker.py new file mode 100644 index 0000000..84faecb --- /dev/null +++ b/workers/arduino_relay_worker.py @@ -0,0 +1,167 @@ +import time +import datetime +import json +import redis +import threading +import sys +import socket +from nanpy import (SerialManager) +from nanpy.serialmanager import SerialManagerError +from nanpy.sockconnection import (SocketManager, SocketManagerError) +sys.path.append('..') + +import variables + +#r = redis.Redis(host='127.0.0.1', port=6379) + +# ToDO Update relay to make a key if one is not set in config + +class ArduinoRelayWorker(): + def __init__(self, config, main_thread_running, system_ready, relay_available, relay_active, node_connected, connection=None): + #self.config = {**config, **self.config} + self.config = config + self.config['pin'] = int(self.config['pin']) #parse possbile strings to avoid errors + + #Events + self.main_thread_running = main_thread_running + self.system_ready = system_ready + self.relay_available = relay_available + self.relay_active = relay_active + + #Dynamic Properties based on config + self.active = False + self.topic = self.config['topic'].replace(" ", "/").lower() if self.config['topic'] is not None else 'mudpi/relay/*' + + #Pubsub Listeners + self.pubsub = variables.r.pubsub() + self.pubsub.subscribe(**{self.topic: self.handleMessage}) + + if node_connected.is_set(): + self.api = ArduinoApi(connection) + self.pin_state_off = self.api.HIGH if self.config['normally_open'] is not None and self.config['normally_open'] else self.api.LOW + self.pin_state_on = self.api.LOW if self.config['normally_open'] is not None and self.config['normally_open'] else self.api.HIGH + self.init() + return + + def init(self): + self.api.pinMode(self.config['pin'], self.api.OUTPUT) + #Close the relay by default, we use the pin state we determined based on the config at init + self.api.digitalWrite(self.config['pin'], self.pin_state_off) + time.sleep(0.1) + + #Feature to restore relay state in case of crash or unexpected shutdown. This will check for last state stored in redis and set relay accordingly + if(self.config.get('restore_last_known_state', None) is not None and self.config.get('restore_last_known_state', False) is True): + if(variables.r.get(self.config['key']+'_state')): + self.api.digitalWrite(self.config['pin'], self.pin_state_on) + print('Restoring Relay \033[1;36m{0} On\033[0;0m'.format(self.config['key'])) + + + print('Node Relay Worker {key}...\t\t\t\033[1;32m Ready\033[0;0m'.format(**self.config)) + return + + def run(self): + t = threading.Thread(target=self.work, args=()) + t.start() + print('Node Relay Worker {key}...\t\t\t\033[1;32m Running\033[0;0m'.format(**self.config)) + return t + + def decodeMessageData(self, message): + if isinstance(message, dict): + #print('Dict Found') + return message + elif isinstance(message.decode('utf-8'), str): + try: + temp = json.loads(message.decode('utf-8')) + #print('Json Found') + return temp + except: + #print('Json Error. Str Found') + return {'event':'Unknown', 'data':message} + else: + #print('Failed to detect type') + return {'event':'Unknown', 'data':message} + + def handleMessage(self, message): + data = message['data'] + if data is not None: + decoded_message = self.decodeMessageData(data) + try: + if decoded_message['event'] == 'Switch': + if decoded_message.get('data', None): + self.relay_active.set() + elif decoded_message.get('data', None) == 0: + self.relay_active.clear() + print('Switch Relay \033[1;36m{0}\033[0;0m state to \033[1;36m{1}\033[0;0m'.format(self.config['key'], decoded_message['data'])) + elif decoded_message['event'] == 'Toggle': + state = 'Off' if self.active else 'On' + if self.relay_active.is_set(): + self.relay_active.clear() + else: + self.relay_active.set() + print('Toggle Relay \033[1;36m{0} {1} \033[0;0m'.format(self.config['key'], state)) + except: + print('Error Decoding Message for Relay {0}'.format(self.config['key'])) + + def elapsedTime(self): + self.time_elapsed = time.perf_counter() - self.time_start + return self.time_elapsed + + def resetElapsedTime(self): + self.time_start = time.perf_counter() + pass + + def turnOn(self): + #Turn on relay if its available + if self.relay_available.is_set(): + if not self.active: + self.api.digitalWrite(self.config['pin'], self.pin_state_on) + message = {'event':'StateChanged', 'data':1} + variables.r.set(self.config['key']+'_state', 1) + variables.r.publish(self.topic, json.dumps(message)) + self.active = True + #self.relay_active.set() This is handled by the redis listener now + self.resetElapsedTime() + + def turnOff(self): + #Turn off volkeye to flip off relay + if self.relay_available.is_set(): + if self.active: + self.api.digitalWrite(self.config['pin'], self.pin_state_off) + message = {'event':'StateChanged', 'data':0} + variables.r.delete(self.config['key']+'_state') + variables.r.publish(self.topic, json.dumps(message)) + #self.relay_active.clear() This is handled by the redis listener now + self.active = False + self.resetElapsedTime() + + def work(self): + self.resetElapsedTime() + while self.main_thread_running.is_set(): + if self.system_ready.is_set(): + + try: + self.pubsub.get_message() + if self.relay_available.is_set(): + if self.relay_active.is_set(): + self.turnOn() + else: + self.turnOff() + else: + self.turnOff() + time.sleep(1) + except: + print("Relay Worker \033[1;36m{key}\033[0;0m \t\033[1;31m Unexpected Error\033[0;0m".format(**self.config)) + + else: + #System not ready relay should be off + self.turnOff() + time.sleep(1) + self.resetElapsedTime() + + time.sleep(0.1) + + + #This is only ran after the main thread is shut down + #Close the pubsub connection + self.pubsub.close() + print("Relay Worker {key} Shutting Down...\t\033[1;32m Complete\033[0;0m".format(**self.config)) \ No newline at end of file diff --git a/workers/arduino_worker.py b/workers/arduino_worker.py index 7270c8a..9fe659e 100644 --- a/workers/arduino_worker.py +++ b/workers/arduino_worker.py @@ -8,6 +8,7 @@ from nanpy.sockconnection import (SocketManager, SocketManagerError) from workers.arduino_control_worker import ArduinoControlWorker from workers.arduino_sensor_worker import ArduinoSensorWorker +from workers.arduino_relay_worker import ArduinoRelayWorker import sys sys.path.append('..') @@ -28,22 +29,52 @@ def __init__(self, config, main_thread_running, system_ready, connection=None): self.node_ready = threading.Event() self.node_connected = threading.Event() #Event to signal if camera can be used self.workers = [] + self.relays = [] + self.relayEvents = {} + self.relay_index = 0 if connection is None: self.connection = self.connect() - if self.config['controls'] is not None: - acw = ArduinoControlWorker(self.config, main_thread_running, system_ready, self.node_connected, self.connection) - self.workers.append(acw) - acw = acw.run() - if acw is not None: - self.threads.append(acw) + try: + if self.config['controls'] is not None: + acw = ArduinoControlWorker(self.config, main_thread_running, system_ready, self.node_connected, self.connection) + self.workers.append(acw) + acw = acw.run() + if acw is not None: + self.threads.append(acw) + except KeyError: + print('No Node Controls Found to Load') - if self.config['sensors'] is not None: - asw = ArduinoSensorWorker(self.config, main_thread_running, system_ready, self.node_connected, self.connection) - self.workers.append(asw) - asw = asw.run() - if asw is not None: - self.threads.append(asw) + try: + if self.config['sensors'] is not None: + asw = ArduinoSensorWorker(self.config, main_thread_running, system_ready, self.node_connected, self.connection) + self.workers.append(asw) + asw = asw.run() + if asw is not None: + self.threads.append(asw) + except KeyError: + print('No Node Sensors Found to Load') + + try: + if self.config['relays'] is not None: + for relay in self.config['relays']: + #Create a threading event for each relay to check status + relayState = { + "available": threading.Event(), #Event to allow relay to activate + "active": threading.Event() #Event to signal relay to open/close + } + #Store the relays under the key or index if no key is found, this way we can reference the right relays + self.relayEvents[relay.get("key", relay_index)] = relayState + #Create sensor worker for a relay + arw = ArduinoRelayWorker(relay, main_thread_running, system_ready, relayState['available'], relayState['active'], self.node_connected, self.connection) + arw = r.run() + #Make the relays available, this event is toggled off elsewhere if we need to disable relays + self.relayState['available'].set() + self.relay_index +=1 + if arw is not None: + self.threads.append(r) + except KeyError: + print('No Node Relays Found to Load') return def connect(self): From 44ec0c537723e071378ae02c94eb49f9f416a51d Mon Sep 17 00:00:00 2001 From: Eric Davisson Date: Tue, 11 Aug 2020 16:16:07 -0500 Subject: [PATCH 15/97] Working on node relay support --- workers/arduino_relay_worker.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/workers/arduino_relay_worker.py b/workers/arduino_relay_worker.py index 84faecb..1f1ca9e 100644 --- a/workers/arduino_relay_worker.py +++ b/workers/arduino_relay_worker.py @@ -150,7 +150,7 @@ def work(self): self.turnOff() time.sleep(1) except: - print("Relay Worker \033[1;36m{key}\033[0;0m \t\033[1;31m Unexpected Error\033[0;0m".format(**self.config)) + print("Node Relay Worker \033[1;36m{key}\033[0;0m \t\033[1;31m Unexpected Error\033[0;0m".format(**self.config)) else: #System not ready relay should be off @@ -164,4 +164,4 @@ def work(self): #This is only ran after the main thread is shut down #Close the pubsub connection self.pubsub.close() - print("Relay Worker {key} Shutting Down...\t\033[1;32m Complete\033[0;0m".format(**self.config)) \ No newline at end of file + print("Node Relay Worker {key} Shutting Down...\t\033[1;32m Complete\033[0;0m".format(**self.config)) \ No newline at end of file From 79a5b85d42fcb67ad1aa9ee5834fbe0c7151ac9e Mon Sep 17 00:00:00 2001 From: Eric Davisson Date: Tue, 11 Aug 2020 16:18:59 -0500 Subject: [PATCH 16/97] Fixing arduino relays --- workers/arduino_worker.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/workers/arduino_worker.py b/workers/arduino_worker.py index 9fe659e..7721815 100644 --- a/workers/arduino_worker.py +++ b/workers/arduino_worker.py @@ -64,7 +64,7 @@ def __init__(self, config, main_thread_running, system_ready, connection=None): "active": threading.Event() #Event to signal relay to open/close } #Store the relays under the key or index if no key is found, this way we can reference the right relays - self.relayEvents[relay.get("key", relay_index)] = relayState + self.relayEvents[relay.get("key", self.relay_index)] = relayState #Create sensor worker for a relay arw = ArduinoRelayWorker(relay, main_thread_running, system_ready, relayState['available'], relayState['active'], self.node_connected, self.connection) arw = r.run() From de859753def4386a8f4823cc715adcc00b9db065 Mon Sep 17 00:00:00 2001 From: Eric Davisson Date: Tue, 11 Aug 2020 16:22:45 -0500 Subject: [PATCH 17/97] Fixes to node relays --- workers/arduino_relay_worker.py | 2 +- workers/arduino_worker.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/workers/arduino_relay_worker.py b/workers/arduino_relay_worker.py index 1f1ca9e..3730240 100644 --- a/workers/arduino_relay_worker.py +++ b/workers/arduino_relay_worker.py @@ -5,7 +5,7 @@ import threading import sys import socket -from nanpy import (SerialManager) +from nanpy import (SerialManager, ArduinoApi) from nanpy.serialmanager import SerialManagerError from nanpy.sockconnection import (SocketManager, SocketManagerError) sys.path.append('..') diff --git a/workers/arduino_worker.py b/workers/arduino_worker.py index 7721815..17da5df 100644 --- a/workers/arduino_worker.py +++ b/workers/arduino_worker.py @@ -67,7 +67,7 @@ def __init__(self, config, main_thread_running, system_ready, connection=None): self.relayEvents[relay.get("key", self.relay_index)] = relayState #Create sensor worker for a relay arw = ArduinoRelayWorker(relay, main_thread_running, system_ready, relayState['available'], relayState['active'], self.node_connected, self.connection) - arw = r.run() + arw = arw.run() #Make the relays available, this event is toggled off elsewhere if we need to disable relays self.relayState['available'].set() self.relay_index +=1 From 45a1074bd2884dcf94fe476787073c605470515e Mon Sep 17 00:00:00 2001 From: Eric Davisson Date: Tue, 11 Aug 2020 16:28:02 -0500 Subject: [PATCH 18/97] Fixes to node relays --- workers/arduino_worker.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/workers/arduino_worker.py b/workers/arduino_worker.py index 17da5df..1ac73f0 100644 --- a/workers/arduino_worker.py +++ b/workers/arduino_worker.py @@ -69,7 +69,7 @@ def __init__(self, config, main_thread_running, system_ready, connection=None): arw = ArduinoRelayWorker(relay, main_thread_running, system_ready, relayState['available'], relayState['active'], self.node_connected, self.connection) arw = arw.run() #Make the relays available, this event is toggled off elsewhere if we need to disable relays - self.relayState['available'].set() + relayState['available'].set() self.relay_index +=1 if arw is not None: self.threads.append(r) From ad82ed0e17b0f069cd406d47fecc6cfded5f616d Mon Sep 17 00:00:00 2001 From: Eric Davisson Date: Tue, 11 Aug 2020 16:29:35 -0500 Subject: [PATCH 19/97] Pass configs through workers --- workers/arduino_relay_worker.py | 4 ++-- workers/arduino_worker.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/workers/arduino_relay_worker.py b/workers/arduino_relay_worker.py index 3730240..5795469 100644 --- a/workers/arduino_relay_worker.py +++ b/workers/arduino_relay_worker.py @@ -17,7 +17,7 @@ # ToDO Update relay to make a key if one is not set in config class ArduinoRelayWorker(): - def __init__(self, config, main_thread_running, system_ready, relay_available, relay_active, node_connected, connection=None): + def __init__(self, config, main_thread_running, system_ready, relay_available, relay_active, node_connected, connection=None, api=None): #self.config = {**config, **self.config} self.config = config self.config['pin'] = int(self.config['pin']) #parse possbile strings to avoid errors @@ -37,7 +37,7 @@ def __init__(self, config, main_thread_running, system_ready, relay_available, r self.pubsub.subscribe(**{self.topic: self.handleMessage}) if node_connected.is_set(): - self.api = ArduinoApi(connection) + self.api = api if api is not None else ArduinoApi(connection) self.pin_state_off = self.api.HIGH if self.config['normally_open'] is not None and self.config['normally_open'] else self.api.LOW self.pin_state_on = self.api.LOW if self.config['normally_open'] is not None and self.config['normally_open'] else self.api.HIGH self.init() diff --git a/workers/arduino_worker.py b/workers/arduino_worker.py index 1ac73f0..bcd02dc 100644 --- a/workers/arduino_worker.py +++ b/workers/arduino_worker.py @@ -66,7 +66,7 @@ def __init__(self, config, main_thread_running, system_ready, connection=None): #Store the relays under the key or index if no key is found, this way we can reference the right relays self.relayEvents[relay.get("key", self.relay_index)] = relayState #Create sensor worker for a relay - arw = ArduinoRelayWorker(relay, main_thread_running, system_ready, relayState['available'], relayState['active'], self.node_connected, self.connection) + arw = ArduinoRelayWorker(relay, main_thread_running, system_ready, relayState['available'], relayState['active'], self.node_connected, self.connection, self.api) arw = arw.run() #Make the relays available, this event is toggled off elsewhere if we need to disable relays relayState['available'].set() From 18250fb58aa89d7ad6a983de2f3b0210be099108 Mon Sep 17 00:00:00 2001 From: Eric Davisson Date: Tue, 11 Aug 2020 16:30:56 -0500 Subject: [PATCH 20/97] Fixes to node relays --- workers/arduino_worker.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/workers/arduino_worker.py b/workers/arduino_worker.py index bcd02dc..1d7df93 100644 --- a/workers/arduino_worker.py +++ b/workers/arduino_worker.py @@ -72,7 +72,7 @@ def __init__(self, config, main_thread_running, system_ready, connection=None): relayState['available'].set() self.relay_index +=1 if arw is not None: - self.threads.append(r) + self.threads.append(arw) except KeyError: print('No Node Relays Found to Load') return From ea763672730cee66c223d44c6ab1f45e216b9c52 Mon Sep 17 00:00:00 2001 From: Eric Davisson Date: Tue, 11 Aug 2020 16:36:57 -0500 Subject: [PATCH 21/97] Update humidity_sensor.py --- sensors/pi/humidity_sensor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sensors/pi/humidity_sensor.py b/sensors/pi/humidity_sensor.py index 78a8df0..085a294 100644 --- a/sensors/pi/humidity_sensor.py +++ b/sensors/pi/humidity_sensor.py @@ -39,7 +39,7 @@ def read(self): if humidity is not None and temperature is not None: variables.r.set(self.key + '_temperature', round(temperature * 1.8 + 32, 2)) variables.r.set(self.key + '_humidity', humidity) - readings = {'temperature': round(temperature * 1.8 + 32, 2), 'humidity': humidity} + readings = {'temperature': round(temperature * 1.8 + 32, 2), 'humidity': round(humidity, 2)} variables.r.set(self.key, json.dumps(readings)) print('Pi Temp:', readings) return readings From ee0c88fcaa0d445fea87adbd60425d86e6f2b85d Mon Sep 17 00:00:00 2001 From: Eric Davisson Date: Tue, 11 Aug 2020 16:59:39 -0500 Subject: [PATCH 22/97] Debugging Node Wifi --- workers/arduino_worker.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/workers/arduino_worker.py b/workers/arduino_worker.py index 1d7df93..eb505f6 100644 --- a/workers/arduino_worker.py +++ b/workers/arduino_worker.py @@ -83,15 +83,16 @@ def connect(self): if self.config.get('use_wifi', False): while attempts > 0 and self.main_thread_running.is_set(): try: - print('\033[1;36m{0}\033[0;0m -> Connecting... \t'.format(self.config["name"], (3-attempts)), end='\r', flush=True) + print('\033[1;36m{0}\033[0;0m -> Connecting... \t'.format(self.config["name"], (3-attempts))) attempts-= 1 conn = SocketManager(host=str(self.config.get('address', 'mudpi.local'))) # Test the connection with api self.api = ArduinoApi(connection=conn) except (SocketManagerError, BrokenPipeError, ConnectionResetError, socket.timeout) as e: print('{name} -> Connecting...\t\t\033[1;33m Timeout\033[0;0m '.format(**self.config)) + print(e); if attempts > 0: - print('{name} -> Preparing Reconnect... \t'.format(**self.config), end='\r', flush=True) + print('{name} -> Preparing Reconnect... \t'.format(**self.config)) else: print('{name} -> Connection Attempts...\t\033[1;31m Failed\033[0;0m '.format(**self.config)) conn = None @@ -161,7 +162,7 @@ def work(self): # Node reconnection cycle if not self.node_connected.is_set(): # Random delay before connections to offset multiple attempts (1-5 min delay) - random_delay = random.randrange(60,300) * delay_multiplier + random_delay = random.randrange(30, self.config.get("max_reconnect_delay", 300)) * delay_multiplier time.sleep(10) print('\033[1;36m'+str(self.config['name']) +'\033[0;0m -> Retrying in '+ '{0}s...'.format(random_delay)+'\t\033[1;33m Pending Reconnect\033[0;0m ') # Two separate checks for main thread event to prevent re-connections during shutdown From 5e7f60c6c89a90200d4036b23d70d3af401b1415 Mon Sep 17 00:00:00 2001 From: Eric Davisson Date: Tue, 11 Aug 2020 17:07:56 -0500 Subject: [PATCH 23/97] Working on nodes wifi --- sensors/arduino/humidity_sensor.py | 1 + 1 file changed, 1 insertion(+) diff --git a/sensors/arduino/humidity_sensor.py b/sensors/arduino/humidity_sensor.py index 697bc61..eaea589 100644 --- a/sensors/arduino/humidity_sensor.py +++ b/sensors/arduino/humidity_sensor.py @@ -40,6 +40,7 @@ def read(self): variables.r.set(self.key + '_temperature', temperature) variables.r.set(self.key + '_humidity', humidity) variables.r.set(self.key, json.dumps(data)) + print('Node Temp:', data) return data def readRaw(self): From 41e4a95d86e87024f332563d0eaa8f8db75892b2 Mon Sep 17 00:00:00 2001 From: Eric Davisson Date: Tue, 11 Aug 2020 17:23:15 -0500 Subject: [PATCH 24/97] Updates to arduino worker --- workers/arduino_sensor_worker.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/workers/arduino_sensor_worker.py b/workers/arduino_sensor_worker.py index bb5a7bf..4b00a51 100644 --- a/workers/arduino_sensor_worker.py +++ b/workers/arduino_sensor_worker.py @@ -103,7 +103,6 @@ def work(self): variables.r.publish(self.channel, json.dumps(message)) except (SerialManagerError, SocketManagerError, BrokenPipeError, ConnectionResetError, OSError, socket.timeout) as e: print('\033[1;36m{name}\033[0;0m -> \033[1;33mSensors Timeout!\033[0;0m'.format(**self.config)) - self.sensors_ready = False self.sensors = [] self.node_connected.clear() time.sleep(15) @@ -113,7 +112,6 @@ def work(self): self.sensors_ready = True else: #Node not connected, sensors not ready. Wait for reconnect - self.sensors_ready = False self.sensors = [] # Main loop delay between cycles From a1d5347b0bce24b497764e0614e6e52d465e7b7b Mon Sep 17 00:00:00 2001 From: Eric Davisson Date: Tue, 11 Aug 2020 17:53:42 -0500 Subject: [PATCH 25/97] Updates to arduino workers --- sensors/arduino/humidity_sensor.py | 1 - workers/arduino_relay_worker.py | 26 +++++++++++++++----------- workers/arduino_sensor_worker.py | 3 ++- 3 files changed, 17 insertions(+), 13 deletions(-) diff --git a/sensors/arduino/humidity_sensor.py b/sensors/arduino/humidity_sensor.py index eaea589..697bc61 100644 --- a/sensors/arduino/humidity_sensor.py +++ b/sensors/arduino/humidity_sensor.py @@ -40,7 +40,6 @@ def read(self): variables.r.set(self.key + '_temperature', temperature) variables.r.set(self.key + '_humidity', humidity) variables.r.set(self.key, json.dumps(data)) - print('Node Temp:', data) return data def readRaw(self): diff --git a/workers/arduino_relay_worker.py b/workers/arduino_relay_worker.py index 5795469..8157ba4 100644 --- a/workers/arduino_relay_worker.py +++ b/workers/arduino_relay_worker.py @@ -138,19 +138,23 @@ def work(self): self.resetElapsedTime() while self.main_thread_running.is_set(): if self.system_ready.is_set(): - - try: - self.pubsub.get_message() - if self.relay_available.is_set(): - if self.relay_active.is_set(): - self.turnOn() + if self.node_connected.is_set(): + try: + self.pubsub.get_message() + if self.relay_available.is_set(): + if self.relay_active.is_set(): + self.turnOn() + else: + self.turnOff() else: self.turnOff() - else: - self.turnOff() - time.sleep(1) - except: - print("Node Relay Worker \033[1;36m{key}\033[0;0m \t\033[1;31m Unexpected Error\033[0;0m".format(**self.config)) + time.sleep(1) + except e: + print("Node Relay Worker \033[1;36m{key}\033[0;0m \t\033[1;31m Unexpected Error\033[0;0m".format(**self.config)) + print(e) + else: + # Node offline + time.sleep(5) else: #System not ready relay should be off diff --git a/workers/arduino_sensor_worker.py b/workers/arduino_sensor_worker.py index 4b00a51..55cf480 100644 --- a/workers/arduino_sensor_worker.py +++ b/workers/arduino_sensor_worker.py @@ -73,6 +73,7 @@ def init_sensors(self, connection=None): new_sensor.init_sensor() self.sensors.append(new_sensor) print('{type} Sensor {pin}...\t\t\t\033[1;32m Ready\033[0;0m'.format(**sensor)) + self.sensors_ready = True except (SerialManagerError, SocketManagerError, BrokenPipeError, ConnectionResetError, OSError, socket.timeout) as e: # Connection error. Reset everything for reconnect self.sensors_ready = False @@ -98,7 +99,7 @@ def work(self): readings[sensor.key] = result #r.set(sensor.get('key', sensor.get('type')), value) - print(readings) + print("Node Readings: ", readings) message['data'] = readings variables.r.publish(self.channel, json.dumps(message)) except (SerialManagerError, SocketManagerError, BrokenPipeError, ConnectionResetError, OSError, socket.timeout) as e: From 051490cef8e8183e42690cc67057d8ef44952f71 Mon Sep 17 00:00:00 2001 From: Eric Davisson Date: Tue, 11 Aug 2020 18:09:45 -0500 Subject: [PATCH 26/97] Working on nodes --- mudpi.py | 1 + workers/arduino_relay_worker.py | 13 +++++++------ workers/arduino_worker.py | 1 + 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/mudpi.py b/mudpi.py index 602cd42..ff4a6a4 100755 --- a/mudpi.py +++ b/mudpi.py @@ -174,6 +174,7 @@ threads.append(t) except KeyError as e: print('Invalid or no Nodes found to Load') + print(e) # Load in Actions diff --git a/workers/arduino_relay_worker.py b/workers/arduino_relay_worker.py index 8157ba4..e32569c 100644 --- a/workers/arduino_relay_worker.py +++ b/workers/arduino_relay_worker.py @@ -36,14 +36,15 @@ def __init__(self, config, main_thread_running, system_ready, relay_available, r self.pubsub = variables.r.pubsub() self.pubsub.subscribe(**{self.topic: self.handleMessage}) - if node_connected.is_set(): - self.api = api if api is not None else ArduinoApi(connection) - self.pin_state_off = self.api.HIGH if self.config['normally_open'] is not None and self.config['normally_open'] else self.api.LOW - self.pin_state_on = self.api.LOW if self.config['normally_open'] is not None and self.config['normally_open'] else self.api.HIGH + if self.node_connected.is_set(): + print('Node Relay Worker {key}...\t\t\033[1;32m Preparing\033[0;0m'.format(**self.config)) self.init() return def init(self): + self.api = api if api is not None else ArduinoApi(connection) + self.pin_state_off = self.api.HIGH if self.config['normally_open'] is not None and self.config['normally_open'] else self.api.LOW + self.pin_state_on = self.api.LOW if self.config['normally_open'] is not None and self.config['normally_open'] else self.api.HIGH self.api.pinMode(self.config['pin'], self.api.OUTPUT) #Close the relay by default, we use the pin state we determined based on the config at init self.api.digitalWrite(self.config['pin'], self.pin_state_off) @@ -56,13 +57,13 @@ def init(self): print('Restoring Relay \033[1;36m{0} On\033[0;0m'.format(self.config['key'])) - print('Node Relay Worker {key}...\t\t\t\033[1;32m Ready\033[0;0m'.format(**self.config)) + print('Node Relay Worker {key}...\t\t\033[1;32m Ready\033[0;0m'.format(**self.config)) return def run(self): t = threading.Thread(target=self.work, args=()) t.start() - print('Node Relay Worker {key}...\t\t\t\033[1;32m Running\033[0;0m'.format(**self.config)) + print('Node Relay Worker {key}...\t\t\033[1;32m Running\033[0;0m'.format(**self.config)) return t def decodeMessageData(self, message): diff --git a/workers/arduino_worker.py b/workers/arduino_worker.py index eb505f6..4d88f29 100644 --- a/workers/arduino_worker.py +++ b/workers/arduino_worker.py @@ -32,6 +32,7 @@ def __init__(self, config, main_thread_running, system_ready, connection=None): self.relays = [] self.relayEvents = {} self.relay_index = 0 + self.api = None if connection is None: self.connection = self.connect() From d250d72fd37196596295d1856d02481f9dff24d2 Mon Sep 17 00:00:00 2001 From: Eric Davisson Date: Tue, 11 Aug 2020 18:13:40 -0500 Subject: [PATCH 27/97] Node bug fixes --- workers/arduino_relay_worker.py | 1 + 1 file changed, 1 insertion(+) diff --git a/workers/arduino_relay_worker.py b/workers/arduino_relay_worker.py index e32569c..63e93a8 100644 --- a/workers/arduino_relay_worker.py +++ b/workers/arduino_relay_worker.py @@ -27,6 +27,7 @@ def __init__(self, config, main_thread_running, system_ready, relay_available, r self.system_ready = system_ready self.relay_available = relay_available self.relay_active = relay_active + self.node_connected = node_connected #Dynamic Properties based on config self.active = False From df8563eb71b97eb7fb1f4d8c5fc8909edf27dfd0 Mon Sep 17 00:00:00 2001 From: Eric Davisson Date: Tue, 11 Aug 2020 18:20:39 -0500 Subject: [PATCH 28/97] Updates to node relay worker --- workers/arduino_relay_worker.py | 28 ++++++++++++++++------------ workers/arduino_worker.py | 7 ++++--- 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/workers/arduino_relay_worker.py b/workers/arduino_relay_worker.py index 63e93a8..4637a7a 100644 --- a/workers/arduino_relay_worker.py +++ b/workers/arduino_relay_worker.py @@ -31,6 +31,7 @@ def __init__(self, config, main_thread_running, system_ready, relay_available, r #Dynamic Properties based on config self.active = False + self.relay_ready = False self.topic = self.config['topic'].replace(" ", "/").lower() if self.config['topic'] is not None else 'mudpi/relay/*' #Pubsub Listeners @@ -57,7 +58,7 @@ def init(self): self.api.digitalWrite(self.config['pin'], self.pin_state_on) print('Restoring Relay \033[1;36m{0} On\033[0;0m'.format(self.config['key'])) - + self.relay_ready = True print('Node Relay Worker {key}...\t\t\033[1;32m Ready\033[0;0m'.format(**self.config)) return @@ -141,19 +142,22 @@ def work(self): while self.main_thread_running.is_set(): if self.system_ready.is_set(): if self.node_connected.is_set(): - try: - self.pubsub.get_message() - if self.relay_available.is_set(): - if self.relay_active.is_set(): - self.turnOn() + if self.relay_ready: + try: + self.pubsub.get_message() + if self.relay_available.is_set(): + if self.relay_active.is_set(): + self.turnOn() + else: + self.turnOff() else: self.turnOff() - else: - self.turnOff() - time.sleep(1) - except e: - print("Node Relay Worker \033[1;36m{key}\033[0;0m \t\033[1;31m Unexpected Error\033[0;0m".format(**self.config)) - print(e) + time.sleep(1) + except e: + print("Node Relay Worker \033[1;36m{key}\033[0;0m \t\033[1;31m Unexpected Error\033[0;0m".format(**self.config)) + print(e) + else: + self.init() else: # Node offline time.sleep(5) diff --git a/workers/arduino_worker.py b/workers/arduino_worker.py index 4d88f29..aa89463 100644 --- a/workers/arduino_worker.py +++ b/workers/arduino_worker.py @@ -40,7 +40,6 @@ def __init__(self, config, main_thread_running, system_ready, connection=None): if self.config['controls'] is not None: acw = ArduinoControlWorker(self.config, main_thread_running, system_ready, self.node_connected, self.connection) self.workers.append(acw) - acw = acw.run() if acw is not None: self.threads.append(acw) except KeyError: @@ -50,7 +49,6 @@ def __init__(self, config, main_thread_running, system_ready, connection=None): if self.config['sensors'] is not None: asw = ArduinoSensorWorker(self.config, main_thread_running, system_ready, self.node_connected, self.connection) self.workers.append(asw) - asw = asw.run() if asw is not None: self.threads.append(asw) except KeyError: @@ -68,7 +66,6 @@ def __init__(self, config, main_thread_running, system_ready, connection=None): self.relayEvents[relay.get("key", self.relay_index)] = relayState #Create sensor worker for a relay arw = ArduinoRelayWorker(relay, main_thread_running, system_ready, relayState['available'], relayState['active'], self.node_connected, self.connection, self.api) - arw = arw.run() #Make the relays available, this event is toggled off elsewhere if we need to disable relays relayState['available'].set() self.relay_index +=1 @@ -76,6 +73,7 @@ def __init__(self, config, main_thread_running, system_ready, connection=None): self.threads.append(arw) except KeyError: print('No Node Relays Found to Load') + return def connect(self): @@ -142,6 +140,9 @@ def resetConnection(self): def run(self): + for worker in self.workers: + worker.run() + t = threading.Thread(target=self.work, args=()) t.start() if self.node_ready.is_set(): From 06a532e6029024a13a08a40e67b2047c94a13227 Mon Sep 17 00:00:00 2001 From: Eric Davisson Date: Tue, 11 Aug 2020 18:30:44 -0500 Subject: [PATCH 29/97] Updates to node workers --- workers/arduino_relay_worker.py | 3 ++- workers/arduino_sensor_worker.py | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/workers/arduino_relay_worker.py b/workers/arduino_relay_worker.py index 4637a7a..a88710e 100644 --- a/workers/arduino_relay_worker.py +++ b/workers/arduino_relay_worker.py @@ -39,11 +39,11 @@ def __init__(self, config, main_thread_running, system_ready, relay_available, r self.pubsub.subscribe(**{self.topic: self.handleMessage}) if self.node_connected.is_set(): - print('Node Relay Worker {key}...\t\t\033[1;32m Preparing\033[0;0m'.format(**self.config)) self.init() return def init(self): + print('Node Relay Worker {key}...\t\t\033[1;32m Preparing\033[0;0m'.format(**self.config)) self.api = api if api is not None else ArduinoApi(connection) self.pin_state_off = self.api.HIGH if self.config['normally_open'] is not None and self.config['normally_open'] else self.api.LOW self.pin_state_on = self.api.LOW if self.config['normally_open'] is not None and self.config['normally_open'] else self.api.HIGH @@ -160,6 +160,7 @@ def work(self): self.init() else: # Node offline + self.relay_ready = False time.sleep(5) else: diff --git a/workers/arduino_sensor_worker.py b/workers/arduino_sensor_worker.py index 55cf480..8c458d7 100644 --- a/workers/arduino_sensor_worker.py +++ b/workers/arduino_sensor_worker.py @@ -114,6 +114,7 @@ def work(self): else: #Node not connected, sensors not ready. Wait for reconnect self.sensors = [] + self.sensors_ready = False # Main loop delay between cycles time.sleep(self.sleep_duration) From 94429131dca7fa5db79f2d09c543080b02dcb516 Mon Sep 17 00:00:00 2001 From: Eric Davisson Date: Tue, 11 Aug 2020 18:32:13 -0500 Subject: [PATCH 30/97] Updates to sensor worker for nodes --- workers/arduino_sensor_worker.py | 1 + 1 file changed, 1 insertion(+) diff --git a/workers/arduino_sensor_worker.py b/workers/arduino_sensor_worker.py index 8c458d7..0907c8d 100644 --- a/workers/arduino_sensor_worker.py +++ b/workers/arduino_sensor_worker.py @@ -45,6 +45,7 @@ def dynamic_import(self, path): def init_sensors(self, connection=None): try: + print('Node Sensor Worker {key}...\t\t\033[1;32m Preparing\033[0;0m'.format(**self.config)) for sensor in self.config['sensors']: if sensor.get('type', None) is not None: #Get the sensor from the sensors folder {sensor name}_sensor.{SensorName}Sensor From 6e0a26654ddd7f7c40295dd72502168450188b35 Mon Sep 17 00:00:00 2001 From: Eric Davisson Date: Tue, 11 Aug 2020 18:34:11 -0500 Subject: [PATCH 31/97] Node relay worker updates --- workers/arduino_relay_worker.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/workers/arduino_relay_worker.py b/workers/arduino_relay_worker.py index a88710e..00c72ad 100644 --- a/workers/arduino_relay_worker.py +++ b/workers/arduino_relay_worker.py @@ -37,6 +37,7 @@ def __init__(self, config, main_thread_running, system_ready, relay_available, r #Pubsub Listeners self.pubsub = variables.r.pubsub() self.pubsub.subscribe(**{self.topic: self.handleMessage}) + self.api = api if self.node_connected.is_set(): self.init() @@ -44,7 +45,7 @@ def __init__(self, config, main_thread_running, system_ready, relay_available, r def init(self): print('Node Relay Worker {key}...\t\t\033[1;32m Preparing\033[0;0m'.format(**self.config)) - self.api = api if api is not None else ArduinoApi(connection) + self.api = self.api if self.api is not None else ArduinoApi(connection) self.pin_state_off = self.api.HIGH if self.config['normally_open'] is not None and self.config['normally_open'] else self.api.LOW self.pin_state_on = self.api.LOW if self.config['normally_open'] is not None and self.config['normally_open'] else self.api.HIGH self.api.pinMode(self.config['pin'], self.api.OUTPUT) From cec39ee9e8ebe2bfcf2231f63f173525bab20307 Mon Sep 17 00:00:00 2001 From: Eric Davisson Date: Tue, 11 Aug 2020 18:45:56 -0500 Subject: [PATCH 32/97] Updates to node workers --- workers/arduino_sensor_worker.py | 2 +- workers/arduino_worker.py | 7 ++----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/workers/arduino_sensor_worker.py b/workers/arduino_sensor_worker.py index 0907c8d..29907c3 100644 --- a/workers/arduino_sensor_worker.py +++ b/workers/arduino_sensor_worker.py @@ -44,8 +44,8 @@ def dynamic_import(self, path): return sensor def init_sensors(self, connection=None): + print('Node Sensor Worker {key}...\t\t\033[1;32m Preparing\033[0;0m'.format(**self.config)) try: - print('Node Sensor Worker {key}...\t\t\033[1;32m Preparing\033[0;0m'.format(**self.config)) for sensor in self.config['sensors']: if sensor.get('type', None) is not None: #Get the sensor from the sensors folder {sensor name}_sensor.{SensorName}Sensor diff --git a/workers/arduino_worker.py b/workers/arduino_worker.py index aa89463..1a21bb1 100644 --- a/workers/arduino_worker.py +++ b/workers/arduino_worker.py @@ -49,8 +49,6 @@ def __init__(self, config, main_thread_running, system_ready, connection=None): if self.config['sensors'] is not None: asw = ArduinoSensorWorker(self.config, main_thread_running, system_ready, self.node_connected, self.connection) self.workers.append(asw) - if asw is not None: - self.threads.append(asw) except KeyError: print('No Node Sensors Found to Load') @@ -69,8 +67,7 @@ def __init__(self, config, main_thread_running, system_ready, connection=None): #Make the relays available, this event is toggled off elsewhere if we need to disable relays relayState['available'].set() self.relay_index +=1 - if arw is not None: - self.threads.append(arw) + self.workers.append(arw) except KeyError: print('No Node Relays Found to Load') @@ -141,7 +138,7 @@ def resetConnection(self): def run(self): for worker in self.workers: - worker.run() + self.threads.append(worker.run()) t = threading.Thread(target=self.work, args=()) t.start() From a03600f7ebba6ea3657fd3aa19e4552907ad2836 Mon Sep 17 00:00:00 2001 From: Eric Davisson Date: Tue, 11 Aug 2020 19:39:35 -0500 Subject: [PATCH 33/97] Fixing node workers --- workers/arduino_worker.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/workers/arduino_worker.py b/workers/arduino_worker.py index 1a21bb1..fc5e3d8 100644 --- a/workers/arduino_worker.py +++ b/workers/arduino_worker.py @@ -49,8 +49,9 @@ def __init__(self, config, main_thread_running, system_ready, connection=None): if self.config['sensors'] is not None: asw = ArduinoSensorWorker(self.config, main_thread_running, system_ready, self.node_connected, self.connection) self.workers.append(asw) - except KeyError: + except KeyError as e: print('No Node Sensors Found to Load') + print(e) try: if self.config['relays'] is not None: From 368b685a911fd1f834ca9e1f0bafa4be693f5bf6 Mon Sep 17 00:00:00 2001 From: Eric Davisson Date: Tue, 11 Aug 2020 19:59:46 -0500 Subject: [PATCH 34/97] Update sensor worker --- package.json | 2 +- workers/arduino_sensor_worker.py | 2 +- workers/arduino_worker.py | 10 +++------- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/package.json b/package.json index 467a79e..f68443b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "mudpi-core", - "version": "0.8.11", + "version": "0.8.13", "description": "Configurable automated smart garden for raspberry pi", "bugs": "https://github.com/mudpi/mudpi-core/issues", "contributors": [ diff --git a/workers/arduino_sensor_worker.py b/workers/arduino_sensor_worker.py index 29907c3..a9df20b 100644 --- a/workers/arduino_sensor_worker.py +++ b/workers/arduino_sensor_worker.py @@ -28,7 +28,7 @@ def __init__(self, config, main_thread_running, system_ready, node_connected, co self.sensors = [] if node_connected.is_set(): self.init_sensors() - self.sensors_ready = True + self.sensors_ready = True return def dynamic_import(self, path): diff --git a/workers/arduino_worker.py b/workers/arduino_worker.py index fc5e3d8..80249ec 100644 --- a/workers/arduino_worker.py +++ b/workers/arduino_worker.py @@ -45,13 +45,9 @@ def __init__(self, config, main_thread_running, system_ready, connection=None): except KeyError: print('No Node Controls Found to Load') - try: - if self.config['sensors'] is not None: - asw = ArduinoSensorWorker(self.config, main_thread_running, system_ready, self.node_connected, self.connection) - self.workers.append(asw) - except KeyError as e: - print('No Node Sensors Found to Load') - print(e) + if self.config['sensors'] is not None: + asw = ArduinoSensorWorker(self.config, main_thread_running, system_ready, self.node_connected, self.connection) + self.workers.append(asw) try: if self.config['relays'] is not None: From 36592c6c3a2cadfaf6576a2376670336033fa864 Mon Sep 17 00:00:00 2001 From: Eric Davisson Date: Wed, 12 Aug 2020 08:55:13 -0500 Subject: [PATCH 35/97] Debugging on arduino --- mudpi.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mudpi.py b/mudpi.py index ff4a6a4..429eb57 100755 --- a/mudpi.py +++ b/mudpi.py @@ -5,6 +5,7 @@ import time import json import sys +import traceback sys.path.append('..') from action import Action from config_load import loadConfigJson @@ -174,6 +175,7 @@ threads.append(t) except KeyError as e: print('Invalid or no Nodes found to Load') + traceback.print_exc() print(e) From 93c12446ebe6de956a8a9b91cbbec37724212a04 Mon Sep 17 00:00:00 2001 From: Eric Davisson Date: Wed, 12 Aug 2020 09:00:17 -0500 Subject: [PATCH 36/97] Cleaning up bugs in node workers --- mudpi.py | 2 -- workers/arduino_sensor_worker.py | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/mudpi.py b/mudpi.py index 429eb57..ff4a6a4 100755 --- a/mudpi.py +++ b/mudpi.py @@ -5,7 +5,6 @@ import time import json import sys -import traceback sys.path.append('..') from action import Action from config_load import loadConfigJson @@ -175,7 +174,6 @@ threads.append(t) except KeyError as e: print('Invalid or no Nodes found to Load') - traceback.print_exc() print(e) diff --git a/workers/arduino_sensor_worker.py b/workers/arduino_sensor_worker.py index a9df20b..b483318 100644 --- a/workers/arduino_sensor_worker.py +++ b/workers/arduino_sensor_worker.py @@ -44,7 +44,7 @@ def dynamic_import(self, path): return sensor def init_sensors(self, connection=None): - print('Node Sensor Worker {key}...\t\t\033[1;32m Preparing\033[0;0m'.format(**self.config)) + print('{name} Sensor Worker...\t\t\033[1;32m Preparing\033[0;0m'.format(**self.config)) try: for sensor in self.config['sensors']: if sensor.get('type', None) is not None: From 23506c68f1ef6c6a030a2639beb923b9813f45d8 Mon Sep 17 00:00:00 2001 From: Eric Davisson Date: Wed, 12 Aug 2020 09:05:47 -0500 Subject: [PATCH 37/97] Fixes to arduino sensor worker --- sensors/arduino/humidity_sensor.py | 2 +- workers/arduino_sensor_worker.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sensors/arduino/humidity_sensor.py b/sensors/arduino/humidity_sensor.py index 697bc61..de1969f 100644 --- a/sensors/arduino/humidity_sensor.py +++ b/sensors/arduino/humidity_sensor.py @@ -25,7 +25,7 @@ def init_sensor(self): sensor_types = { '11': DHT.DHT11, '22': DHT.DHT22, '2301': DHT.AM2301 } - if len(self.type) == 3 and self.type in sensor_types: + if self.type in sensor_types: self.sensor = sensor_types[self.type] else: # print('Sensor Type Error: Defaulting to DHT11') diff --git a/workers/arduino_sensor_worker.py b/workers/arduino_sensor_worker.py index b483318..bd8fc27 100644 --- a/workers/arduino_sensor_worker.py +++ b/workers/arduino_sensor_worker.py @@ -67,7 +67,7 @@ def init_sensors(self, connection=None): # optional sensor variables # Model is specific to DHT modules to specify DHT11(11) DHT22(22) or DHT2301(21) if sensor.get('model'): - sensor_kwargs['model'] = sensor.get('model') + sensor_kwargs['model'] = str(sensor.get('model')) new_sensor = imported_sensor(**sensor_kwargs) From a97e1676d45852ef33766254ec53e20a23c354bb Mon Sep 17 00:00:00 2001 From: Eric Davisson Date: Wed, 12 Aug 2020 10:30:18 -0500 Subject: [PATCH 38/97] Fixing delays for esp32 nodes --- mudpi.py | 3 +-- workers/arduino_worker.py | 13 ++++++++++--- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/mudpi.py b/mudpi.py index ff4a6a4..c066f72 100755 --- a/mudpi.py +++ b/mudpi.py @@ -173,8 +173,7 @@ if t is not None: threads.append(t) except KeyError as e: - print('Invalid or no Nodes found to Load') - print(e) + print('Nodes found to Load or Invalid Config Format') # Load in Actions diff --git a/workers/arduino_worker.py b/workers/arduino_worker.py index 80249ec..a4a5bff 100644 --- a/workers/arduino_worker.py +++ b/workers/arduino_worker.py @@ -42,12 +42,17 @@ def __init__(self, config, main_thread_running, system_ready, connection=None): self.workers.append(acw) if acw is not None: self.threads.append(acw) + time.sleep(3) except KeyError: print('No Node Controls Found to Load') - if self.config['sensors'] is not None: - asw = ArduinoSensorWorker(self.config, main_thread_running, system_ready, self.node_connected, self.connection) - self.workers.append(asw) + try: + if self.config['sensors'] is not None: + asw = ArduinoSensorWorker(self.config, main_thread_running, system_ready, self.node_connected, self.connection) + self.workers.append(asw) + time.sleep(3) + except KeyError: + print('No Node Sensors Found to Load') try: if self.config['relays'] is not None: @@ -65,6 +70,7 @@ def __init__(self, config, main_thread_running, system_ready, connection=None): relayState['available'].set() self.relay_index +=1 self.workers.append(arw) + time.sleep(3) except KeyError: print('No Node Relays Found to Load') @@ -136,6 +142,7 @@ def resetConnection(self): def run(self): for worker in self.workers: self.threads.append(worker.run()) + time.sleep(4) t = threading.Thread(target=self.work, args=()) t.start() From ba61cc5ae347763797ef814762875318ffd8e895 Mon Sep 17 00:00:00 2001 From: Eric Davisson Date: Wed, 12 Aug 2020 11:34:00 -0500 Subject: [PATCH 39/97] Debugging arduino worker --- workers/arduino_worker.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/workers/arduino_worker.py b/workers/arduino_worker.py index a4a5bff..68d85a0 100644 --- a/workers/arduino_worker.py +++ b/workers/arduino_worker.py @@ -46,14 +46,6 @@ def __init__(self, config, main_thread_running, system_ready, connection=None): except KeyError: print('No Node Controls Found to Load') - try: - if self.config['sensors'] is not None: - asw = ArduinoSensorWorker(self.config, main_thread_running, system_ready, self.node_connected, self.connection) - self.workers.append(asw) - time.sleep(3) - except KeyError: - print('No Node Sensors Found to Load') - try: if self.config['relays'] is not None: for relay in self.config['relays']: @@ -74,6 +66,14 @@ def __init__(self, config, main_thread_running, system_ready, connection=None): except KeyError: print('No Node Relays Found to Load') + try: + if self.config['sensors'] is not None: + asw = ArduinoSensorWorker(self.config, main_thread_running, system_ready, self.node_connected, self.connection) + self.workers.append(asw) + time.sleep(3) + except KeyError: + print('No Node Sensors Found to Load') + return def connect(self): From 68a5b6bf0cfb7f7fe1791c1adb9bcf69403ace59 Mon Sep 17 00:00:00 2001 From: Eric Davisson Date: Wed, 12 Aug 2020 14:46:04 -0500 Subject: [PATCH 40/97] Debugging sockets on nodes --- sensors/arduino/humidity_sensor.py | 1 + workers/arduino_sensor_worker.py | 3 ++- workers/arduino_worker.py | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/sensors/arduino/humidity_sensor.py b/sensors/arduino/humidity_sensor.py index de1969f..dba3fad 100644 --- a/sensors/arduino/humidity_sensor.py +++ b/sensors/arduino/humidity_sensor.py @@ -31,6 +31,7 @@ def init_sensor(self): # print('Sensor Type Error: Defaulting to DHT11') self.sensor = DHT.DHT11 self.dht = DHT(self.pin, self.sensor, connection=self.connection) + print("DHT Init") def read(self): #Pass true to read in american degrees :) diff --git a/workers/arduino_sensor_worker.py b/workers/arduino_sensor_worker.py index bd8fc27..790c063 100644 --- a/workers/arduino_sensor_worker.py +++ b/workers/arduino_sensor_worker.py @@ -15,7 +15,7 @@ #r = redis.Redis(host='127.0.0.1', port=6379) class ArduinoSensorWorker(): - def __init__(self, config, main_thread_running, system_ready, node_connected, connection=None): + def __init__(self, config, main_thread_running, system_ready, node_connected, connection=None, api=None): #self.config = {**config, **self.config} self.config = config self.main_thread_running = main_thread_running @@ -25,6 +25,7 @@ def __init__(self, config, main_thread_running, system_ready, node_connected, co self.sensors_ready = False self.node_connected = node_connected self.connection = connection + self.api = api self.sensors = [] if node_connected.is_set(): self.init_sensors() diff --git a/workers/arduino_worker.py b/workers/arduino_worker.py index 68d85a0..b9ce1df 100644 --- a/workers/arduino_worker.py +++ b/workers/arduino_worker.py @@ -68,7 +68,7 @@ def __init__(self, config, main_thread_running, system_ready, connection=None): try: if self.config['sensors'] is not None: - asw = ArduinoSensorWorker(self.config, main_thread_running, system_ready, self.node_connected, self.connection) + asw = ArduinoSensorWorker(self.config, main_thread_running, system_ready, self.node_connected, self.connection, self.api) self.workers.append(asw) time.sleep(3) except KeyError: From 9300c3233a20c091af36356727ac0fb8921116e9 Mon Sep 17 00:00:00 2001 From: Eric Davisson Date: Wed, 12 Aug 2020 16:32:17 -0500 Subject: [PATCH 41/97] Pass api connection to sensors --- sensors/arduino/sensor.py | 4 ++-- workers/arduino_relay_worker.py | 6 +++--- workers/arduino_sensor_worker.py | 3 +++ 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/sensors/arduino/sensor.py b/sensors/arduino/sensor.py index 3c5313b..9fa174b 100644 --- a/sensors/arduino/sensor.py +++ b/sensors/arduino/sensor.py @@ -8,13 +8,13 @@ # Base sensor class to extend all other arduino sensors from. class Sensor(): - def __init__(self, pin, name='Sensor', connection=default_connection, analog_pin_mode=False, key=None): + def __init__(self, pin, name='Sensor', connection=default_connection, analog_pin_mode=False, key=None, api=None): self.pin = pin self.name = name self.key = key.replace(" ", "_").lower() if key is not None else self.name.replace(" ", "_").lower() self.analog_pin_mode = analog_pin_mode self.connection = connection - self.api = ArduinoApi(connection) + self.api = api if api is not None else ArduinoApi(connection) return def init_sensor(self): diff --git a/workers/arduino_relay_worker.py b/workers/arduino_relay_worker.py index 00c72ad..e548721 100644 --- a/workers/arduino_relay_worker.py +++ b/workers/arduino_relay_worker.py @@ -60,13 +60,13 @@ def init(self): print('Restoring Relay \033[1;36m{0} On\033[0;0m'.format(self.config['key'])) self.relay_ready = True - print('Node Relay Worker {key}...\t\t\033[1;32m Ready\033[0;0m'.format(**self.config)) + print('Node Relay {key}...\t\t\033[1;32m Ready\033[0;0m'.format(**self.config)) return def run(self): t = threading.Thread(target=self.work, args=()) t.start() - print('Node Relay Worker {key}...\t\t\033[1;32m Running\033[0;0m'.format(**self.config)) + print('Node Relay {key}...\t\t\033[1;32m Running\033[0;0m'.format(**self.config)) return t def decodeMessageData(self, message): @@ -176,4 +176,4 @@ def work(self): #This is only ran after the main thread is shut down #Close the pubsub connection self.pubsub.close() - print("Node Relay Worker {key} Shutting Down...\t\033[1;32m Complete\033[0;0m".format(**self.config)) \ No newline at end of file + print("Node Relay {key} Shutting Down...\t\033[1;32m Complete\033[0;0m".format(**self.config)) \ No newline at end of file diff --git a/workers/arduino_sensor_worker.py b/workers/arduino_sensor_worker.py index 790c063..f6425fc 100644 --- a/workers/arduino_sensor_worker.py +++ b/workers/arduino_sensor_worker.py @@ -69,15 +69,18 @@ def init_sensors(self, connection=None): # Model is specific to DHT modules to specify DHT11(11) DHT22(22) or DHT2301(21) if sensor.get('model'): sensor_kwargs['model'] = str(sensor.get('model')) + sensor_kwargs['api'] = self.api new_sensor = imported_sensor(**sensor_kwargs) + print('{type} Sensor {pin}...\t\t\t\033[1;32m Preparing\033[0;0m'.format(**sensor)) new_sensor.init_sensor() self.sensors.append(new_sensor) print('{type} Sensor {pin}...\t\t\t\033[1;32m Ready\033[0;0m'.format(**sensor)) self.sensors_ready = True except (SerialManagerError, SocketManagerError, BrokenPipeError, ConnectionResetError, OSError, socket.timeout) as e: # Connection error. Reset everything for reconnect + print('\033[1;36m{name}\033[0;0m -> \033[1;33mSensors Init Timeout!\033[0;0m'.format(**self.config)) self.sensors_ready = False self.node_connected.clear() self.sensors = [] From 79cb8a86e9cf74bfcf2009a3a44f9176b215606a Mon Sep 17 00:00:00 2001 From: Eric Davisson Date: Wed, 12 Aug 2020 16:34:00 -0500 Subject: [PATCH 42/97] Passing api to humidity sensor --- sensors/arduino/humidity_sensor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sensors/arduino/humidity_sensor.py b/sensors/arduino/humidity_sensor.py index dba3fad..fad8da3 100644 --- a/sensors/arduino/humidity_sensor.py +++ b/sensors/arduino/humidity_sensor.py @@ -15,7 +15,7 @@ class HumiditySensor(Sensor): - def __init__(self, pin, name='HumiditySensor', key=None, connection=default_connection, model='11'): + def __init__(self, pin, name='HumiditySensor', key=None, connection=default_connection, model='11', api=None): super().__init__(pin, name=name, key=key, connection=connection) self.type = model #DHT11 or DHT22 maybe AM2302 return From 1730ac3b4913ffe28f53507acb0e97dc39cbc95f Mon Sep 17 00:00:00 2001 From: Eric Davisson Date: Wed, 12 Aug 2020 16:40:34 -0500 Subject: [PATCH 43/97] Debugging arduino DHT --- sensors/arduino/humidity_sensor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sensors/arduino/humidity_sensor.py b/sensors/arduino/humidity_sensor.py index fad8da3..619f7ec 100644 --- a/sensors/arduino/humidity_sensor.py +++ b/sensors/arduino/humidity_sensor.py @@ -30,8 +30,8 @@ def init_sensor(self): else: # print('Sensor Type Error: Defaulting to DHT11') self.sensor = DHT.DHT11 - self.dht = DHT(self.pin, self.sensor, connection=self.connection) print("DHT Init") + self.dht = DHT(self.pin, self.sensor, connection=self.connection) def read(self): #Pass true to read in american degrees :) From 49fd1b00987a87e3c7f261d30a607f28aedc56eb Mon Sep 17 00:00:00 2001 From: Eric Davisson Date: Thu, 13 Aug 2020 11:54:59 -0500 Subject: [PATCH 44/97] v0.8.13 - Cleaned up Float Sensor - Revamped Light Sensor to Work with Complete Modules - Added Support for BME680 - Added better config defaults for DHT sensor - Added Relays to Nodes - Added an `ArduinoRelayWorker` to handle node relays --- server/mudpi_server.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/mudpi_server.py b/server/mudpi_server.py index 64111ec..d87a649 100644 --- a/server/mudpi_server.py +++ b/server/mudpi_server.py @@ -9,7 +9,7 @@ class MudpiServer(object): - def __init__(self, system_running, host='127.0.0.1', port=6601): + def __init__(self, system_running, host='127.0.0.1', port=7002): self.port = int(port) self.host = host self.system_running = system_running @@ -57,7 +57,7 @@ def listenToClient(self, client, address): if __name__ == "__main__": host = '127.0.0.1' - port = 6002 + port = 7002 server = MudpiServer(host, port) server.listen(); while True: From 604d6fd5c9d380493de4c023a83e3485468caac3 Mon Sep 17 00:00:00 2001 From: Eric Davisson Date: Thu, 13 Aug 2020 12:48:39 -0500 Subject: [PATCH 45/97] Added I2C Worker Added Worker for I2C Added BME680 Sensor --- mudpi.py | 5 ++ sensors/pi/i2c/__init__.py | 0 sensors/pi/{ => i2c}/bme680_sensor.py | 5 +- sensors/pi/i2c/sensor.py | 36 +++++++++++ workers/pi_i2c_worker.py | 92 +++++++++++++++++++++++++++ 5 files changed, 135 insertions(+), 3 deletions(-) create mode 100644 sensors/pi/i2c/__init__.py rename sensors/pi/{ => i2c}/bme680_sensor.py (91%) create mode 100644 sensors/pi/i2c/sensor.py create mode 100644 workers/pi_i2c_worker.py diff --git a/mudpi.py b/mudpi.py index c066f72..0609709 100755 --- a/mudpi.py +++ b/mudpi.py @@ -15,6 +15,8 @@ from workers.trigger_worker import TriggerWorker from workers.pi_sensor_worker import PiSensorWorker from workers.pi_control_worker import PiControlWorker +from workers.pi_i2c_worker import PiI2CWorker + try: # Does this prevent the need to install the module if you dont use it? from workers.arduino_worker import ArduinoWorker @@ -119,6 +121,9 @@ elif worker['type'] == "control": pw = PiControlWorker(worker, main_thread_running, system_ready) print('Loading Pi Control Worker...') + elif worker['type'] == "i2c": + pw = PiI2CWorker(worker, main_thread_running, system_ready) + print('Loading Pi I2C Worker...') elif worker['type'] == "relay": # Add Relay Worker Here for Better Config Control print('Loading Pi Relay Worker...') diff --git a/sensors/pi/i2c/__init__.py b/sensors/pi/i2c/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/sensors/pi/bme680_sensor.py b/sensors/pi/i2c/bme680_sensor.py similarity index 91% rename from sensors/pi/bme680_sensor.py rename to sensors/pi/i2c/bme680_sensor.py index c30d2ab..efa1881 100644 --- a/sensors/pi/bme680_sensor.py +++ b/sensors/pi/i2c/bme680_sensor.py @@ -17,13 +17,12 @@ class Bme680Sensor(Sensor): - def __init__(self, pin = None, name='PressureSensor', key=None): - super().__init__(pin, name=name, key=key) + def __init__(self, address = None, name='PressureSensor', key=None): + super().__init__(address, name=name, key=key) return def init_sensor(self): #Initialize the sensor here (i.e. set pin mode, get addresses, etc) this gets called by the worker - self.i2c = I2C(board.SCL, board.SDA) self.sensor = adafruit_bme680.Adafruit_BME680_I2C(self.i2c, debug=False) # change this to match the location's pressure (hPa) at sea level self.sensor.sea_level_pressure = 1013.25 diff --git a/sensors/pi/i2c/sensor.py b/sensors/pi/i2c/sensor.py new file mode 100644 index 0000000..ac2185a --- /dev/null +++ b/sensors/pi/i2c/sensor.py @@ -0,0 +1,36 @@ +import time +import json +import redis +import RPi.GPIO as GPIO + +#PIN MODE : OUT | IN + +class Sensor(): + + def __init__(self, address, name='Sensor', key=None): + self.address = address + self.name = name + self.key = key.replace(" ", "_").lower() if key is not None else self.name.replace(" ", "_").lower() + self.gpio = GPIO + self.i2c = I2C(board.SCL, board.SDA) + return + + def init_sensor(self): + #Initialize the sensor here (i.e. set pin mode, get addresses, etc) + #GPIO.setmode(GPIO.BCM) + #GPIO.setup(pin, GPIO.IN) + pass + + def read(self): + #Read the sensor(s), parse the data and store it in redis if redis is configured + #GPIO.input(pin) + pass + + def readRaw(self): + #Read the sensor(s) but return the raw data, useful for debugging + pass + + def readPin(self): + #Read the pin from the ardiuno. Can be analog or digital based on "analog_pin_mode" + data = self.gpio.input(self.pin) + return data \ No newline at end of file diff --git a/workers/pi_i2c_worker.py b/workers/pi_i2c_worker.py new file mode 100644 index 0000000..4cdafc7 --- /dev/null +++ b/workers/pi_i2c_worker.py @@ -0,0 +1,92 @@ +import time +import datetime +import json +import redis +import threading +import sys +sys.path.append('..') +from sensors.pi.i2c.bme680_sensor import (Bme680Sensor) + +import variables + +#r = redis.Redis(host='127.0.0.1', port=6379) +# def clamp(n, smallest, largest): return max(smallest, min(n, largest)) + +class PiI2CWorker(): + def __init__(self, config, main_thread_running, system_ready): + #self.config = {**config, **self.config} + self.config = config + self.channel = config.get('channel', 'i2c').replace(" ", "_").lower() + self.sleep_duration = config.get('sleep_duration', 30) + self.main_thread_running = main_thread_running + self.system_ready = system_ready + self.sensors = [] + self.init_sensors() + return + + def dynamic_import(self, name): + #Split path of the class folder structure: {sensor name}_sensor . {SensorName}Sensor + components = name.split('.') + #Dynamically import root of component path + module = __import__(components[0]) + #Get component attributes + for component in components[1:]: + module = getattr(module, component) + return module + + def init_sensors(self): + for sensor in self.config['sensors']: + if sensor.get('type', None) is not None: + #Get the sensor from the sensors folder {sensor name}_sensor.{SensorName}Sensor + sensor_type = 'sensors.pi.i2c.' + sensor.get('type').lower() + '_sensor.' + sensor.get('type').capitalize() + 'Sensor' + + imported_sensor = self.dynamic_import(sensor_type) + + # Define default kwargs for all sensor types, conditionally include optional variables below if they exist + sensor_kwargs = { + 'name' : sensor.get('name', sensor.get('type')), + 'address' : int(sensor.get('address', 00)), + 'key' : sensor.get('key', None) + } + + # optional sensor variables + # Model is specific to DHT modules to specify DHT11 DHT22 or DHT2302 + if sensor.get('model'): + sensor_kwargs['model'] = str(sensor.get('model')) + + new_sensor = imported_sensor(**sensor_kwargs) + new_sensor.init_sensor() + + #Set the sensor type and determine if the readings are critical to operations + new_sensor.type = sensor.get('type').lower() + + self.sensors.append(new_sensor) + print('{type} Sensor (Pi) {pin}...\t\t\033[1;32m Ready\033[0;0m'.format(**sensor)) + return + + def run(self): + t = threading.Thread(target=self.work, args=()) + t.start() + print('Pi I2C Sensor Worker [' + str(len(self.sensors)) + ' Sensors]...\t\t\033[1;32m Running\033[0;0m') + return t + + def work(self): + + while self.main_thread_running.is_set(): + if self.system_ready.is_set(): + message = {'event':'PiSensorUpdate'} + readings = {} + + for sensor in self.sensors: + result = sensor.read() + readings[sensor.key] = result + variables.r.set(sensor.key, json.dumps(result)) + + message['data'] = readings + print("Reaadings I2C: ", readings); + variables.r.publish(self.channel, json.dumps(message)) + time.sleep(self.sleep_duration) + + time.sleep(2) + #This is only ran after the main thread is shut down + print("Pi I2C Sensor Worker Shutting Down...\t\033[1;32m Complete\033[0;0m") \ No newline at end of file From 0f4b0e148bd29bb993f1b1d3ddd26c0b9f666460 Mon Sep 17 00:00:00 2001 From: Eric Davisson Date: Thu, 13 Aug 2020 12:51:42 -0500 Subject: [PATCH 46/97] Cleanup old sensors --- workers/pi_sensor_worker.py | 1 - 1 file changed, 1 deletion(-) diff --git a/workers/pi_sensor_worker.py b/workers/pi_sensor_worker.py index 4d1bd33..3a469fd 100644 --- a/workers/pi_sensor_worker.py +++ b/workers/pi_sensor_worker.py @@ -7,7 +7,6 @@ sys.path.append('..') from sensors.pi.float_sensor import (FloatSensor) from sensors.pi.humidity_sensor import (HumiditySensor) -from sensors.pi.bme680_sensor import (Bme680Sensor) import variables From 8a28672a745a569f5e6708aaae2a19f5abb15616 Mon Sep 17 00:00:00 2001 From: Eric Davisson Date: Thu, 13 Aug 2020 12:53:18 -0500 Subject: [PATCH 47/97] Update base I2C sensor --- sensors/pi/i2c/sensor.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sensors/pi/i2c/sensor.py b/sensors/pi/i2c/sensor.py index ac2185a..bcd2df5 100644 --- a/sensors/pi/i2c/sensor.py +++ b/sensors/pi/i2c/sensor.py @@ -1,6 +1,8 @@ import time import json import redis +import board +from busio import I2C import RPi.GPIO as GPIO #PIN MODE : OUT | IN From 660dac7d9dd890c842697c433f8cd71dc3608f62 Mon Sep 17 00:00:00 2001 From: Eric Davisson Date: Thu, 13 Aug 2020 12:57:27 -0500 Subject: [PATCH 48/97] Fix for debug on i2c worker --- workers/pi_i2c_worker.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/workers/pi_i2c_worker.py b/workers/pi_i2c_worker.py index 4cdafc7..4c2b271 100644 --- a/workers/pi_i2c_worker.py +++ b/workers/pi_i2c_worker.py @@ -61,7 +61,7 @@ def init_sensors(self): new_sensor.type = sensor.get('type').lower() self.sensors.append(new_sensor) - print('{type} Sensor (Pi) {pin}...\t\t\033[1;32m Ready\033[0;0m'.format(**sensor)) + print('{type} Sensor (Pi) {address}...\t\t\033[1;32m Ready\033[0;0m'.format(**sensor)) return def run(self): From e27c6841ab495ac43907df6301b94b5d6dbbf269 Mon Sep 17 00:00:00 2001 From: Eric Davisson Date: Thu, 13 Aug 2020 13:18:45 -0500 Subject: [PATCH 49/97] Workers Refactoring --- mudpi.py | 54 +++++++++++++++++++------------- sensors/pi/i2c/bme680_sensor.py | 2 +- workers/arduino_relay_worker.py | 2 +- workers/arduino_sensor_worker.py | 5 ++- workers/arduino_worker.py | 4 +-- workers/pi_i2c_worker.py | 2 +- 6 files changed, 39 insertions(+), 30 deletions(-) diff --git a/mudpi.py b/mudpi.py index 0609709..d542b91 100755 --- a/mudpi.py +++ b/mudpi.py @@ -91,6 +91,8 @@ relays = [] relayEvents = {} relay_index = 0 + workers = [] + nodes = [] new_messages_waiting = threading.Event() #Event to signal LCD to pull new messages main_thread_running = threading.Event() #Event to signal workers to close @@ -104,12 +106,12 @@ # Worker for Camera try: c = CameraWorker(CONFIGS['camera'], main_thread_running, system_ready, camera_available) - print('Loading Pi Camera Worker') - c = c.run() - threads.append(c) + workers.append(c) + # c = c.run() + # threads.append(c) camera_available.set() except KeyError: - print('No Camera Found to Load') + print('MudPi Pi Camera...\t\t\033[1;31m Disabled\033[0;0m') # Workers for pi (Sensors, Controls, Relays) try: @@ -129,12 +131,12 @@ print('Loading Pi Relay Worker...') else: raise Exception("Unknown Worker Type: " + worker['type']) - pw = pw.run() - if pw is not None: - threads.append(pw) + workers.append(pw) + # pw = pw.run() + # if pw is not None: + # threads.append(pw) except KeyError: - print('No Pi Workers Found to Load or Invalid Type') - + print('MudPi Pi Workers...\t\t\033[1;31m Disabled\033[0;0m') # Worker for relays attached to pi try: @@ -148,14 +150,15 @@ relayEvents[relay.get("key", relay_index)] = relayState #Create sensor worker for a relay r = RelayWorker(relay, main_thread_running, system_ready, relayState['available'], relayState['active']) - r = r.run() + workers.append(r) + # r = r.run() #Make the relays available, this event is toggled off elsewhere if we need to disable relays relayState['available'].set() relay_index +=1 - if r is not None: - threads.append(r) + # if r is not None: + # threads.append(r) except KeyError: - print('No Relays Found to Load') + print('MudPi Relays Workers...\t\t\033[1;31m Disabled\033[0;0m') # Worker for nodes attached to pi via serial or wifi[esp8266] # Supported nodes: arduinos, esp8266, ADC-MCP3xxx, probably others @@ -174,11 +177,12 @@ print('Error Loading MCP3xxx library. Did you pip3 install -r requirements.txt;?') else: raise Exception("Unknown Node Type: " + node['type']) - t = t.run() - if t is not None: - threads.append(t) + nodes.append(t) + # t = t.run() + # if t is not None: + # threads.append(t) except KeyError as e: - print('Nodes found to Load or Invalid Config Format') + print('MudPi Node Workers...\t\t\033[1;31m Disabled\033[0;0m') # Load in Actions @@ -188,17 +192,17 @@ a.init_action() actions[a.key] = a except KeyError: - print('No Actions Found to Load') + print('MudPi Action...\t\t\t\033[1;31m Disabled\033[0;0m') # Worker for Triggers try: t = TriggerWorker(CONFIGS['triggers'], main_thread_running, system_ready, actions) print('Loading Triggers...') - t = t.run() - threads.append(t) + workers.append(t) + # t = t.run() + # threads.append(t) except KeyError: - print('No Triggers Found to Load') - + print('MudPi Triggers...\t\t\t\033[1;31m Disabled\033[0;0m') #Decided not to build server worker (this is replaced with nodejs, expressjs) #Maybe use this for internal communication across devices if using wireless @@ -217,6 +221,12 @@ def server_worker(): print('No Server Config Found to Load') + print('MudPi Garden Control...\t\t\t\033[1;32m Initialized\033[0;0m') + for worker in workers: + t = worker.run() + threads.append(t) + time.sleep(.5) + time.sleep(.5) print('MudPi Garden Control...\t\t\t\033[1;32m Online\033[0;0m') print('_________________________________________________') diff --git a/sensors/pi/i2c/bme680_sensor.py b/sensors/pi/i2c/bme680_sensor.py index efa1881..12245b6 100644 --- a/sensors/pi/i2c/bme680_sensor.py +++ b/sensors/pi/i2c/bme680_sensor.py @@ -45,7 +45,7 @@ def read(self): variables.r.set(self.key + '_altitude', altitude) readings = {'temperature': temperature, 'humidity': humidity, 'pressure': pressure, 'gas': gas, 'altitude': altitude} variables.r.set(self.key, json.dumps(readings)) - print('BME680:', readings) + # print('BME680:', readings) return readings else: print('Failed to get reading [BME680]. Try again!') diff --git a/workers/arduino_relay_worker.py b/workers/arduino_relay_worker.py index e548721..a815114 100644 --- a/workers/arduino_relay_worker.py +++ b/workers/arduino_relay_worker.py @@ -66,7 +66,7 @@ def init(self): def run(self): t = threading.Thread(target=self.work, args=()) t.start() - print('Node Relay {key}...\t\t\033[1;32m Running\033[0;0m'.format(**self.config)) + print('Node Relay {key} Worker...\t\t\033[1;32m Running\033[0;0m'.format(**self.config)) return t def decodeMessageData(self, message): diff --git a/workers/arduino_sensor_worker.py b/workers/arduino_sensor_worker.py index f6425fc..4819042 100644 --- a/workers/arduino_sensor_worker.py +++ b/workers/arduino_sensor_worker.py @@ -73,14 +73,13 @@ def init_sensors(self, connection=None): new_sensor = imported_sensor(**sensor_kwargs) - print('{type} Sensor {pin}...\t\t\t\033[1;32m Preparing\033[0;0m'.format(**sensor)) + # print('{type} Sensor {pin}...\t\t\t\033[1;32m Preparing\033[0;0m'.format(**sensor)) new_sensor.init_sensor() self.sensors.append(new_sensor) - print('{type} Sensor {pin}...\t\t\t\033[1;32m Ready\033[0;0m'.format(**sensor)) + # print('{type} Sensor {pin}...\t\t\t\033[1;32m Ready\033[0;0m'.format(**sensor)) self.sensors_ready = True except (SerialManagerError, SocketManagerError, BrokenPipeError, ConnectionResetError, OSError, socket.timeout) as e: # Connection error. Reset everything for reconnect - print('\033[1;36m{name}\033[0;0m -> \033[1;33mSensors Init Timeout!\033[0;0m'.format(**self.config)) self.sensors_ready = False self.node_connected.clear() self.sensors = [] diff --git a/workers/arduino_worker.py b/workers/arduino_worker.py index b9ce1df..134da7c 100644 --- a/workers/arduino_worker.py +++ b/workers/arduino_worker.py @@ -89,7 +89,7 @@ def connect(self): self.api = ArduinoApi(connection=conn) except (SocketManagerError, BrokenPipeError, ConnectionResetError, socket.timeout) as e: print('{name} -> Connecting...\t\t\033[1;33m Timeout\033[0;0m '.format(**self.config)) - print(e); + # print(e); if attempts > 0: print('{name} -> Preparing Reconnect... \t'.format(**self.config)) else: @@ -165,7 +165,7 @@ def work(self): # Node reconnection cycle if not self.node_connected.is_set(): # Random delay before connections to offset multiple attempts (1-5 min delay) - random_delay = random.randrange(30, self.config.get("max_reconnect_delay", 300)) * delay_multiplier + random_delay = (random.randrange(30, self.config.get("max_reconnect_delay", 300)) * delay_multiplier) / 2 time.sleep(10) print('\033[1;36m'+str(self.config['name']) +'\033[0;0m -> Retrying in '+ '{0}s...'.format(random_delay)+'\t\033[1;33m Pending Reconnect\033[0;0m ') # Two separate checks for main thread event to prevent re-connections during shutdown diff --git a/workers/pi_i2c_worker.py b/workers/pi_i2c_worker.py index 4c2b271..a9316cf 100644 --- a/workers/pi_i2c_worker.py +++ b/workers/pi_i2c_worker.py @@ -67,7 +67,7 @@ def init_sensors(self): def run(self): t = threading.Thread(target=self.work, args=()) t.start() - print('Pi I2C Sensor Worker [' + str(len(self.sensors)) + ' Sensors]...\t\t\033[1;32m Running\033[0;0m') + print('Pi I2C Sensor Worker [' + str(len(self.sensors)) + ' Sensors]...\t\033[1;32m Running\033[0;0m') return t def work(self): From 8ef55ea864312af774ee3c707135f5931df5775e Mon Sep 17 00:00:00 2001 From: Eric Davisson Date: Thu, 13 Aug 2020 13:31:52 -0500 Subject: [PATCH 50/97] Code refactoring --- mudpi.py | 53 ++++++++++++++++-------------- sensors/pi/bme680_sensor.py | 57 +++++++++++++++++++++++++++++++++ sensors/pi/humidity_sensor.py | 1 - server/mudpi_server.py | 2 +- workers/adc_worker.py | 2 +- workers/arduino_relay_worker.py | 2 +- workers/arduino_worker.py | 2 +- workers/camera_worker.py | 2 +- workers/lcd_worker.py | 2 +- workers/pi_control_worker.py | 2 +- workers/pi_i2c_worker.py | 4 +-- workers/pi_sensor_worker.py | 6 ++-- workers/pump_worker.py | 2 +- workers/relay_worker.py | 2 +- workers/sensor_worker.py | 2 +- workers/trigger_worker.py | 2 +- 16 files changed, 102 insertions(+), 41 deletions(-) create mode 100644 sensors/pi/bme680_sensor.py diff --git a/mudpi.py b/mudpi.py index d542b91..51d5a6d 100755 --- a/mudpi.py +++ b/mudpi.py @@ -106,12 +106,13 @@ # Worker for Camera try: c = CameraWorker(CONFIGS['camera'], main_thread_running, system_ready, camera_available) + print('MudPi Camera...\t\t\t\033[1;32m Initializing\033[0;0m') workers.append(c) # c = c.run() # threads.append(c) camera_available.set() except KeyError: - print('MudPi Pi Camera...\t\t\033[1;31m Disabled\033[0;0m') + print('MudPi Pi Camera...\t\t\t\033[1;31m Disabled\033[0;0m') # Workers for pi (Sensors, Controls, Relays) try: @@ -119,16 +120,16 @@ # Create worker for worker if worker['type'] == "sensor": pw = PiSensorWorker(worker, main_thread_running, system_ready) - print('Loading Pi Sensor Worker...') + print('MudPi Sensors...\t\t\t\033[1;32m Initializing\033[0;0m') elif worker['type'] == "control": pw = PiControlWorker(worker, main_thread_running, system_ready) - print('Loading Pi Control Worker...') + print('MudPi Controls...\t\t\t\033[1;32m Initializing\033[0;0m') elif worker['type'] == "i2c": pw = PiI2CWorker(worker, main_thread_running, system_ready) - print('Loading Pi I2C Worker...') + print('MudPi I2C...\t\t\t\033[1;32m Initializing\033[0;0m') elif worker['type'] == "relay": # Add Relay Worker Here for Better Config Control - print('Loading Pi Relay Worker...') + print('MudPi Relay...\t\t\t\033[1;32m Initializing\033[0;0m') else: raise Exception("Unknown Worker Type: " + worker['type']) workers.append(pw) @@ -160,6 +161,29 @@ except KeyError: print('MudPi Relays Workers...\t\t\033[1;31m Disabled\033[0;0m') + + + # Load in Actions + try: + for action in CONFIGS["actions"]: + print('MudPi Actions...\t\t\t\033[1;32m Initializing\033[0;0m') + a = Action(action) + a.init_action() + actions[a.key] = a + except KeyError: + print('MudPi Actions...\t\t\t\033[1;31m Disabled\033[0;0m') + + # Worker for Triggers + try: + t = TriggerWorker(CONFIGS['triggers'], main_thread_running, system_ready, actions) + print('MudPi Triggers...\t\t\t\033[1;32m Initializing\033[0;0m') + workers.append(t) + # t = t.run() + # threads.append(t) + except KeyError: + print('MudPi Triggers...\t\t\t\033[1;31m Disabled\033[0;0m') + + # Worker for nodes attached to pi via serial or wifi[esp8266] # Supported nodes: arduinos, esp8266, ADC-MCP3xxx, probably others try: @@ -185,25 +209,6 @@ print('MudPi Node Workers...\t\t\033[1;31m Disabled\033[0;0m') - # Load in Actions - try: - for action in CONFIGS["actions"]: - a = Action(action) - a.init_action() - actions[a.key] = a - except KeyError: - print('MudPi Action...\t\t\t\033[1;31m Disabled\033[0;0m') - - # Worker for Triggers - try: - t = TriggerWorker(CONFIGS['triggers'], main_thread_running, system_ready, actions) - print('Loading Triggers...') - workers.append(t) - # t = t.run() - # threads.append(t) - except KeyError: - print('MudPi Triggers...\t\t\t\033[1;31m Disabled\033[0;0m') - #Decided not to build server worker (this is replaced with nodejs, expressjs) #Maybe use this for internal communication across devices if using wireless def server_worker(): diff --git a/sensors/pi/bme680_sensor.py b/sensors/pi/bme680_sensor.py new file mode 100644 index 0000000..c30d2ab --- /dev/null +++ b/sensors/pi/bme680_sensor.py @@ -0,0 +1,57 @@ +import time +import json +import redis +from .sensor import Sensor +import RPi.GPIO as GPIO +import board +from busio import I2C +import adafruit_bme680 + +import sys +sys.path.append('..') + +import variables + +#r = redis.Redis(host='127.0.0.1', port=6379) +#PIN MODE : OUT | IN + +class Bme680Sensor(Sensor): + + def __init__(self, pin = None, name='PressureSensor', key=None): + super().__init__(pin, name=name, key=key) + return + + def init_sensor(self): + #Initialize the sensor here (i.e. set pin mode, get addresses, etc) this gets called by the worker + self.i2c = I2C(board.SCL, board.SDA) + self.sensor = adafruit_bme680.Adafruit_BME680_I2C(self.i2c, debug=False) + # change this to match the location's pressure (hPa) at sea level + self.sensor.sea_level_pressure = 1013.25 + return + + def read(self): + #Read the sensor(s), parse the data and store it in redis if redis is configured + + temperature = round((self.sensor.temperature - 5) * 1.8 + 32, 2) + gas = self.sensor.gas + humidity = round(self.sensor.humidity, 1) + pressure = round(self.sensor.pressure, 2) + altitude = round(self.sensor.altitude, 3) + + if humidity is not None and temperature is not None: + variables.r.set(self.key + '_temperature', temperature) + variables.r.set(self.key + '_humidity', humidity) + variables.r.set(self.key + '_gas', gas) + variables.r.set(self.key + '_pressure', pressure) + variables.r.set(self.key + '_altitude', altitude) + readings = {'temperature': temperature, 'humidity': humidity, 'pressure': pressure, 'gas': gas, 'altitude': altitude} + variables.r.set(self.key, json.dumps(readings)) + print('BME680:', readings) + return readings + else: + print('Failed to get reading [BME680]. Try again!') + + + def readRaw(self): + #Read the sensor(s) but return the raw data, useful for debugging + return self.read() diff --git a/sensors/pi/humidity_sensor.py b/sensors/pi/humidity_sensor.py index 085a294..238705e 100644 --- a/sensors/pi/humidity_sensor.py +++ b/sensors/pi/humidity_sensor.py @@ -41,7 +41,6 @@ def read(self): variables.r.set(self.key + '_humidity', humidity) readings = {'temperature': round(temperature * 1.8 + 32, 2), 'humidity': round(humidity, 2)} variables.r.set(self.key, json.dumps(readings)) - print('Pi Temp:', readings) return readings else: print('Failed to get reading. Try again!') diff --git a/server/mudpi_server.py b/server/mudpi_server.py index d87a649..f9d0a21 100644 --- a/server/mudpi_server.py +++ b/server/mudpi_server.py @@ -24,7 +24,7 @@ def __init__(self, system_running, host='127.0.0.1', port=7002): def listen(self): self.sock.listen(10) #number of clients to listen for - print('MudPi Server...\t\t\t\t\033[1;32m Running\033[0;0m ') + print('MudPi Server...\t\t\t\t\033[1;32m Online\033[0;0m ') while self.system_running.is_set(): try: client, address = self.sock.accept() diff --git a/workers/adc_worker.py b/workers/adc_worker.py index 58aaa51..87d3e95 100644 --- a/workers/adc_worker.py +++ b/workers/adc_worker.py @@ -85,7 +85,7 @@ def run(self): t = threading.Thread(target=self.work, args=()) t.start() print(str(self.config['name']) + ' Node Worker [' + str( - len(self.config['sensors'])) + ' Sensors]...\t\033[1;32m Running\033[0;0m') + len(self.config['sensors'])) + ' Sensors]...\t\033[1;32m Online\033[0;0m') return t else: print("Node Connection...\t\t\t\033[1;31m Failed\033[0;0m") diff --git a/workers/arduino_relay_worker.py b/workers/arduino_relay_worker.py index a815114..deebf5f 100644 --- a/workers/arduino_relay_worker.py +++ b/workers/arduino_relay_worker.py @@ -66,7 +66,7 @@ def init(self): def run(self): t = threading.Thread(target=self.work, args=()) t.start() - print('Node Relay {key} Worker...\t\t\033[1;32m Running\033[0;0m'.format(**self.config)) + print('Node Relay {key} Worker...\t\t\033[1;32m Online\033[0;0m'.format(**self.config)) return t def decodeMessageData(self, message): diff --git a/workers/arduino_worker.py b/workers/arduino_worker.py index 134da7c..8e64bfb 100644 --- a/workers/arduino_worker.py +++ b/workers/arduino_worker.py @@ -147,7 +147,7 @@ def run(self): t = threading.Thread(target=self.work, args=()) t.start() if self.node_ready.is_set(): - print(str(self.config['name']) +' Node Worker '+ '[S: ' + str(len(self.config['sensors'])) + ']' + '[C: ' + str(len(self.config['controls'])) + ']...\t\033[1;32m Running\033[0;0m') + print(str(self.config['name']) +' Node Worker '+ '[S: ' + str(len(self.config['sensors'])) + ']' + '[C: ' + str(len(self.config['controls'])) + ']...\t\033[1;32m Online\033[0;0m') else: print(str(self.config['name']) +'...\t\t\t\t\033[1;33m Pending Reconnect\033[0;0m ') return t diff --git a/workers/camera_worker.py b/workers/camera_worker.py index 3a6341b..f49152e 100644 --- a/workers/camera_worker.py +++ b/workers/camera_worker.py @@ -67,7 +67,7 @@ def run(self): t.start() self.listener = threading.Thread(target=self.listen, args=()) self.listener.start() - print('Camera Worker...\t\t\t\033[1;32m Running\033[0;0m') + print('Camera Worker...\t\t\t\033[1;32m Online\033[0;0m') return t def wait(self): diff --git a/workers/lcd_worker.py b/workers/lcd_worker.py index c377019..e216b5b 100644 --- a/workers/lcd_worker.py +++ b/workers/lcd_worker.py @@ -65,7 +65,7 @@ def __init__(self, new_messages_waiting, main_thread_running, system_ready): def run(self): t = threading.Thread(target=self.process_loop, args=()) t.start() - print('LCD Worker...\t\t\t\t\033[1;32m Running\033[0;0m') + print('LCD Worker...\t\t\t\t\033[1;32m Online\033[0;0m') return t def process_loop(self): diff --git a/workers/pi_control_worker.py b/workers/pi_control_worker.py index 6bf0663..ed1b5de 100644 --- a/workers/pi_control_worker.py +++ b/workers/pi_control_worker.py @@ -69,7 +69,7 @@ def init_controls(self): def run(self): t = threading.Thread(target=self.work, args=()) t.start() - print('Pi Control Worker [' + str(len(self.config['controls'])) + ' Controls]...\t\033[1;32m Running\033[0;0m') + print('Pi Control Worker [' + str(len(self.config['controls'])) + ' Controls]...\t\033[1;32m Online\033[0;0m') return t def work(self): diff --git a/workers/pi_i2c_worker.py b/workers/pi_i2c_worker.py index a9316cf..babcd8c 100644 --- a/workers/pi_i2c_worker.py +++ b/workers/pi_i2c_worker.py @@ -61,13 +61,13 @@ def init_sensors(self): new_sensor.type = sensor.get('type').lower() self.sensors.append(new_sensor) - print('{type} Sensor (Pi) {address}...\t\t\033[1;32m Ready\033[0;0m'.format(**sensor)) + # print('{type} Sensor (Pi) {address}...\t\t\033[1;32m Ready\033[0;0m'.format(**sensor)) return def run(self): t = threading.Thread(target=self.work, args=()) t.start() - print('Pi I2C Sensor Worker [' + str(len(self.sensors)) + ' Sensors]...\t\033[1;32m Running\033[0;0m') + print('Pi I2C Sensor Worker [' + str(len(self.sensors)) + ' Sensors]...\t\033[1;32m Online\033[0;0m') return t def work(self): diff --git a/workers/pi_sensor_worker.py b/workers/pi_sensor_worker.py index 3a469fd..298c945 100644 --- a/workers/pi_sensor_worker.py +++ b/workers/pi_sensor_worker.py @@ -67,13 +67,13 @@ def init_sensors(self): new_sensor.critical = False self.sensors.append(new_sensor) - print('{type} Sensor (Pi) {pin}...\t\t\033[1;32m Ready\033[0;0m'.format(**sensor)) + # print('{type} Sensor (Pi) {pin}...\t\t\033[1;32m Ready\033[0;0m'.format(**sensor)) return def run(self): t = threading.Thread(target=self.work, args=()) t.start() - print('Pi Sensor Worker [' + str(len(self.sensors)) + ' Sensors]...\t\t\033[1;32m Running\033[0;0m') + print('Pi Sensor Worker [' + str(len(self.sensors)) + ' Sensors]...\t\t\033[1;32m Online\033[0;0m') return t def work(self): @@ -99,7 +99,7 @@ def work(self): #self.pump_ready.clear() - #print(readings) + print(readings) message['data'] = readings variables.r.publish(self.channel, json.dumps(message)) time.sleep(self.sleep_duration) diff --git a/workers/pump_worker.py b/workers/pump_worker.py index d4bcaa0..0672f70 100644 --- a/workers/pump_worker.py +++ b/workers/pump_worker.py @@ -36,7 +36,7 @@ def init(self): def run(self): t = threading.Thread(target=self.work, args=()) t.start() - print('Pump Worker...\t\t\t\t\033[1;32m Running\033[0;0m') + print('Pump Worker...\t\t\t\t\033[1;32m Online\033[0;0m') return t def elapsedTime(self): diff --git a/workers/relay_worker.py b/workers/relay_worker.py index c70f28f..9e2a602 100644 --- a/workers/relay_worker.py +++ b/workers/relay_worker.py @@ -58,7 +58,7 @@ def init(self): def run(self): t = threading.Thread(target=self.work, args=()) t.start() - print('Relay Worker {key}...\t\t\t\033[1;32m Running\033[0;0m'.format(**self.config)) + print('Relay Worker {key}...\t\t\t\033[1;32m Online\033[0;0m'.format(**self.config)) return t def decodeMessageData(self, message): diff --git a/workers/sensor_worker.py b/workers/sensor_worker.py index 0580479..d069645 100644 --- a/workers/sensor_worker.py +++ b/workers/sensor_worker.py @@ -101,7 +101,7 @@ def run(self): if self.node_ready: t = threading.Thread(target=self.work, args=()) t.start() - print(str(self.config['name']) +' Node Worker [' + str(len(self.config['sensors'])) + ' Sensors]...\t\033[1;32m Running\033[0;0m') + print(str(self.config['name']) +' Node Worker [' + str(len(self.config['sensors'])) + ' Sensors]...\t\033[1;32m Online\033[0;0m') return t else: print("Node Connection...\t\t\t\033[1;31m Failed\033[0;0m") diff --git a/workers/trigger_worker.py b/workers/trigger_worker.py index a24cf7e..96bc941 100644 --- a/workers/trigger_worker.py +++ b/workers/trigger_worker.py @@ -126,7 +126,7 @@ def init_trigger(self, config, trigger_index, group=None): def run(self): t = threading.Thread(target=self.work, args=()) t.start() - print('Trigger Worker [' + str(len(self.config)) + ' Triggers]...\t\t\033[1;32m Running\033[0;0m') + print('Trigger Worker [' + str(len(self.config)) + ' Triggers]...\t\t\033[1;32m Online\033[0;0m') return t def work(self): From 5216e89ef9507cbdf865ea127d66cd65158906da Mon Sep 17 00:00:00 2001 From: Eric Davisson Date: Thu, 13 Aug 2020 13:37:40 -0500 Subject: [PATCH 51/97] Refactoring arduino worker --- mudpi.py | 6 ++++-- workers/arduino_worker.py | 6 +++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/mudpi.py b/mudpi.py index 51d5a6d..771a30d 100755 --- a/mudpi.py +++ b/mudpi.py @@ -126,7 +126,7 @@ print('MudPi Controls...\t\t\t\033[1;32m Initializing\033[0;0m') elif worker['type'] == "i2c": pw = PiI2CWorker(worker, main_thread_running, system_ready) - print('MudPi I2C...\t\t\t\033[1;32m Initializing\033[0;0m') + print('MudPi I2C...\t\t\t\t\033[1;32m Initializing\033[0;0m') elif worker['type'] == "relay": # Add Relay Worker Here for Better Config Control print('MudPi Relay...\t\t\t\033[1;32m Initializing\033[0;0m') @@ -183,7 +183,7 @@ except KeyError: print('MudPi Triggers...\t\t\t\033[1;31m Disabled\033[0;0m') - + # Worker for nodes attached to pi via serial or wifi[esp8266] # Supported nodes: arduinos, esp8266, ADC-MCP3xxx, probably others try: @@ -191,11 +191,13 @@ # Create worker for node if node['type'] == "arduino": if NANPY_ENABLED: + print('MudPi Arduino Workers...\t\t\033[1;32m Initializing\033[0;0m') t = ArduinoWorker(node, main_thread_running, system_ready) else: print('Error Loading Nanpy library. Did you pip3 install -r requirements.txt?') elif node['type'] == "ADC-MCP3008": if MCP_ENABLED: + print('MudPi ADC Workers...\t\t\033[1;32m Initializing\033[0;0m') t = ADCMCP3008Worker(node, main_thread_running, system_ready) else: print('Error Loading MCP3xxx library. Did you pip3 install -r requirements.txt;?') diff --git a/workers/arduino_worker.py b/workers/arduino_worker.py index 8e64bfb..469f8d0 100644 --- a/workers/arduino_worker.py +++ b/workers/arduino_worker.py @@ -44,7 +44,7 @@ def __init__(self, config, main_thread_running, system_ready, connection=None): self.threads.append(acw) time.sleep(3) except KeyError: - print('No Node Controls Found to Load') + print('{name} Node Controls...\t\t\033[1;31m Disabled\033[0;0m'.format(**self.config)) try: if self.config['relays'] is not None: @@ -64,7 +64,7 @@ def __init__(self, config, main_thread_running, system_ready, connection=None): self.workers.append(arw) time.sleep(3) except KeyError: - print('No Node Relays Found to Load') + print('{name} Node Relays...\t\t\033[1;31m Disabled\033[0;0m'.format(**self.config)) try: if self.config['sensors'] is not None: @@ -72,7 +72,7 @@ def __init__(self, config, main_thread_running, system_ready, connection=None): self.workers.append(asw) time.sleep(3) except KeyError: - print('No Node Sensors Found to Load') + print('{name} Node Sensors...\t\t\033[1;31m Disabled\033[0;0m'.format(**self.config)) return From 78039a1bf6e8361508b27d738caf15ab93003cfc Mon Sep 17 00:00:00 2001 From: Eric Davisson Date: Thu, 13 Aug 2020 13:45:38 -0500 Subject: [PATCH 52/97] v0.8.14 - Refactored MudPi Init Process - Refactored Workers --- mudpi.py | 4 ++++ package.json | 2 +- sensors/arduino/humidity_sensor.py | 1 - workers/arduino_relay_worker.py | 3 +-- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/mudpi.py b/mudpi.py index 771a30d..3217f87 100755 --- a/mudpi.py +++ b/mudpi.py @@ -233,6 +233,10 @@ def server_worker(): t = worker.run() threads.append(t) time.sleep(.5) + for node in nodes: + t = node.run() + threads.append(t) + time.sleep(.5) time.sleep(.5) print('MudPi Garden Control...\t\t\t\033[1;32m Online\033[0;0m') diff --git a/package.json b/package.json index f68443b..7cd542e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "mudpi-core", - "version": "0.8.13", + "version": "0.8.14", "description": "Configurable automated smart garden for raspberry pi", "bugs": "https://github.com/mudpi/mudpi-core/issues", "contributors": [ diff --git a/sensors/arduino/humidity_sensor.py b/sensors/arduino/humidity_sensor.py index 619f7ec..dde1dcb 100644 --- a/sensors/arduino/humidity_sensor.py +++ b/sensors/arduino/humidity_sensor.py @@ -30,7 +30,6 @@ def init_sensor(self): else: # print('Sensor Type Error: Defaulting to DHT11') self.sensor = DHT.DHT11 - print("DHT Init") self.dht = DHT(self.pin, self.sensor, connection=self.connection) def read(self): diff --git a/workers/arduino_relay_worker.py b/workers/arduino_relay_worker.py index deebf5f..2a0af72 100644 --- a/workers/arduino_relay_worker.py +++ b/workers/arduino_relay_worker.py @@ -44,7 +44,7 @@ def __init__(self, config, main_thread_running, system_ready, relay_available, r return def init(self): - print('Node Relay Worker {key}...\t\t\033[1;32m Preparing\033[0;0m'.format(**self.config)) + print('{name} Relay Worker {key}...\t\t\033[1;32m Initializing\033[0;0m'.format(**self.config)) self.api = self.api if self.api is not None else ArduinoApi(connection) self.pin_state_off = self.api.HIGH if self.config['normally_open'] is not None and self.config['normally_open'] else self.api.LOW self.pin_state_on = self.api.LOW if self.config['normally_open'] is not None and self.config['normally_open'] else self.api.HIGH @@ -60,7 +60,6 @@ def init(self): print('Restoring Relay \033[1;36m{0} On\033[0;0m'.format(self.config['key'])) self.relay_ready = True - print('Node Relay {key}...\t\t\033[1;32m Ready\033[0;0m'.format(**self.config)) return def run(self): From eab4495818d05d0870f25f9ada06ed5a9d89bb95 Mon Sep 17 00:00:00 2001 From: Eric Davisson Date: Thu, 13 Aug 2020 20:00:38 -0500 Subject: [PATCH 53/97] v0.8.14 Added LCD Worker Started Worker Refactor Big Code Cleanup Support for LCD Character Screens I2C --- mudpi.py | 116 +++++++++------------------ requirements.txt | 3 +- sensors/pi/bme680_sensor.py | 57 ------------- sensors/pi/i2c/bme680_sensor.py | 7 -- workers/pi_i2c_worker.py | 33 +++----- workers/pi_lcd_worker.py | 137 ++++++++++++++++++++++++++++++++ workers/relay_worker.py | 2 +- workers/worker.py | 74 +++++++++++++++++ 8 files changed, 261 insertions(+), 168 deletions(-) delete mode 100644 sensors/pi/bme680_sensor.py create mode 100644 workers/pi_lcd_worker.py create mode 100644 workers/worker.py diff --git a/mudpi.py b/mudpi.py index 3217f87..c6d105d 100755 --- a/mudpi.py +++ b/mudpi.py @@ -15,46 +15,44 @@ from workers.trigger_worker import TriggerWorker from workers.pi_sensor_worker import PiSensorWorker from workers.pi_control_worker import PiControlWorker -from workers.pi_i2c_worker import PiI2CWorker - +from workers.pi_i2c_worker import PiI2CWorker try: - # Does this prevent the need to install the module if you dont use it? from workers.arduino_worker import ArduinoWorker NANPY_ENABLED = True except ImportError: NANPY_ENABLED = False try: - # Does this prevent the need to install the module if you dont use it? from workers.adc_worker import ADCMCP3008Worker MCP_ENABLED = True except ImportError: MCP_ENABLED = False - import variables -# __ __ _ _____ _ -#| \/ | | | __ (_) -#| \ / |_ _ __| | |__) | -#| |\/| | | | |/ _` | ___/ | -#| | | | |_| | (_| | | | | -#|_| |_|\__,_|\__,_|_| |_| -# https://mudpi.app - +############################## +# MudPi Core +# Author: Eric Davisson (@theDavisson) +# https://mudpi.app +# MudPi Core is a python library to gather sensor readings, control components, +# and manage devices using a Raspberry Pi on an event based system using redis. +# CONFIGS = {} PROGRAM_RUNNING = True +threads = [] +actions = {} +relays = [] +relayEvents = {} +relay_index = 0 +workers = [] +nodes = [] print(chr(27) + "[2J") print('Loading MudPi Configs...\r', end="", flush=True) -#load the configuration CONFIGS = loadConfigJson() -#Waiting for redis and services to be running +# Waiting for redis and services to be running time.sleep(5) -print('Loading MudPi Configs...\t\033[1;32m Complete\033[0;0m') -time.sleep(1) - -#Clear the console if its open for debugging +print('Loading MudPi Configs...\t\033[1;32m Complete\033[0;0m') print(chr(27) + "[2J") -#Print a display logo for startup +# Print a display logo for startup print("\033[1;32m") print(' __ __ _ _____ _ ') print('| \/ | | | __ (_)') @@ -65,7 +63,8 @@ print('_________________________________________________') print('') print('Eric Davisson @theDavisson') -print('Version: ', CONFIGS.get('version', '0.8.11')) +print('https://mudpi.app') +print('Version: ', CONFIGS.get('version', '0.8.14')) print('\033[0;0m') if CONFIGS['debug'] is True: @@ -81,25 +80,17 @@ GPIO.setwarnings(False) GPIO.setmode(GPIO.BCM) GPIO.cleanup() - #Pause for GPIO to finish + # Pause for GPIO to finish time.sleep(0.1) print('Initializing Garden Control...\t\t\033[1;32m Complete\033[0;0m') print('Preparing Threads for Workers\r', end="", flush=True) - threads = [] - actions = {} - relays = [] - relayEvents = {} - relay_index = 0 - workers = [] - nodes = [] - - new_messages_waiting = threading.Event() #Event to signal LCD to pull new messages - main_thread_running = threading.Event() #Event to signal workers to close - system_ready = threading.Event() #Event to tell workers to begin working - camera_available = threading.Event() #Event to signal if camera can be used - main_thread_running.set() #Main event to tell workers to run/shutdown + new_messages_waiting = threading.Event() # Event to signal LCD to pull new messages + main_thread_running = threading.Event() # Event to signal workers to close + system_ready = threading.Event() # Event to tell workers to begin working + camera_available = threading.Event() # Event to signal if camera can be used + main_thread_running.set() # Main event to tell workers to run/shutdown time.sleep(0.1) print('Preparing Threads for Workers...\t\033[1;32m Complete\033[0;0m') @@ -108,13 +99,11 @@ c = CameraWorker(CONFIGS['camera'], main_thread_running, system_ready, camera_available) print('MudPi Camera...\t\t\t\033[1;32m Initializing\033[0;0m') workers.append(c) - # c = c.run() - # threads.append(c) camera_available.set() except KeyError: print('MudPi Pi Camera...\t\t\t\033[1;31m Disabled\033[0;0m') - # Workers for pi (Sensors, Controls, Relays) + # Workers for pi (Sensors, Controls, Relays, I2C) try: for worker in CONFIGS['workers']: # Create worker for worker @@ -133,36 +122,25 @@ else: raise Exception("Unknown Worker Type: " + worker['type']) workers.append(pw) - # pw = pw.run() - # if pw is not None: - # threads.append(pw) except KeyError: print('MudPi Pi Workers...\t\t\033[1;31m Disabled\033[0;0m') # Worker for relays attached to pi try: for relay in CONFIGS['relays']: - #Create a threading event for each relay to check status relayState = { - "available": threading.Event(), #Event to allow relay to activate - "active": threading.Event() #Event to signal relay to open/close + "available": threading.Event(), # Event to allow relay to activate + "active": threading.Event() # Event to signal relay to open/close } - #Store the relays under the key or index if no key is found, this way we can reference the right relays relayEvents[relay.get("key", relay_index)] = relayState - #Create sensor worker for a relay r = RelayWorker(relay, main_thread_running, system_ready, relayState['available'], relayState['active']) workers.append(r) - # r = r.run() - #Make the relays available, this event is toggled off elsewhere if we need to disable relays + # Make the relays available, this event is toggled off elsewhere if we need to disable relays relayState['available'].set() relay_index +=1 - # if r is not None: - # threads.append(r) except KeyError: print('MudPi Relays Workers...\t\t\033[1;31m Disabled\033[0;0m') - - # Load in Actions try: for action in CONFIGS["actions"]: @@ -178,17 +156,14 @@ t = TriggerWorker(CONFIGS['triggers'], main_thread_running, system_ready, actions) print('MudPi Triggers...\t\t\t\033[1;32m Initializing\033[0;0m') workers.append(t) - # t = t.run() - # threads.append(t) except KeyError: print('MudPi Triggers...\t\t\t\033[1;31m Disabled\033[0;0m') - # Worker for nodes attached to pi via serial or wifi[esp8266] - # Supported nodes: arduinos, esp8266, ADC-MCP3xxx, probably others + # Worker for nodes attached to pi via serial or wifi[esp8266, esp32] + # Supported nodes: arduinos, esp8266, ADC-MCP3xxx, probably others (esp32 with custom nanpy fork) try: for node in CONFIGS['nodes']: - # Create worker for node if node['type'] == "arduino": if NANPY_ENABLED: print('MudPi Arduino Workers...\t\t\033[1;32m Initializing\033[0;0m') @@ -204,18 +179,9 @@ else: raise Exception("Unknown Node Type: " + node['type']) nodes.append(t) - # t = t.run() - # if t is not None: - # threads.append(t) except KeyError as e: print('MudPi Node Workers...\t\t\033[1;31m Disabled\033[0;0m') - - #Decided not to build server worker (this is replaced with nodejs, expressjs) - #Maybe use this for internal communication across devices if using wireless - def server_worker(): - server.listen() - try: if (CONFIGS['server'] is not None): print('MudPi Server...\t\t\t\t\033[1;33m Starting\033[0;0m', end='\r', flush=True) @@ -246,9 +212,7 @@ def server_worker(): system_message = {'event':'SystemStarted', 'data':1} variables.r.publish('mudpi', json.dumps(system_message)) - - #Hold the program here until its time to graceful shutdown - #This is our pump cycle check, Using redis to determine if pump should activate + # Hold the program here until its time to graceful shutdown while PROGRAM_RUNNING: # Main program loop # add logging or other system operations here... @@ -258,26 +222,20 @@ def server_worker(): PROGRAM_RUNNING = False finally: print('MudPi Shutting Down...') - #Perform any cleanup tasks here... + # Perform any cleanup tasks here... - #load a client on the server to clear it from waiting - # sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - #sock.connect((CONFIGS['SERVER_HOST'], int(CONFIGS['SERVER_PORT']))) - # try: server.sock.shutdown(socket.SHUT_RDWR) except: pass - # time.sleep(1) - # sock.close() - #Clear main running event to signal threads to close + # Clear main running event to signal threads to close main_thread_running.clear() - #Shutdown the camera loop + # Shutdown the camera loop camera_available.clear() - #Join all our threads for shutdown + # Join all our threads for shutdown for thread in threads: thread.join() diff --git a/requirements.txt b/requirements.txt index c65c3ea..d2bb6fd 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,4 +5,5 @@ adafruit-blinka pycron redis picamera -adafruit-circuitpython-bme680 \ No newline at end of file +adafruit-circuitpython-bme680 +adafruit-circuitpython-charlcd \ No newline at end of file diff --git a/sensors/pi/bme680_sensor.py b/sensors/pi/bme680_sensor.py deleted file mode 100644 index c30d2ab..0000000 --- a/sensors/pi/bme680_sensor.py +++ /dev/null @@ -1,57 +0,0 @@ -import time -import json -import redis -from .sensor import Sensor -import RPi.GPIO as GPIO -import board -from busio import I2C -import adafruit_bme680 - -import sys -sys.path.append('..') - -import variables - -#r = redis.Redis(host='127.0.0.1', port=6379) -#PIN MODE : OUT | IN - -class Bme680Sensor(Sensor): - - def __init__(self, pin = None, name='PressureSensor', key=None): - super().__init__(pin, name=name, key=key) - return - - def init_sensor(self): - #Initialize the sensor here (i.e. set pin mode, get addresses, etc) this gets called by the worker - self.i2c = I2C(board.SCL, board.SDA) - self.sensor = adafruit_bme680.Adafruit_BME680_I2C(self.i2c, debug=False) - # change this to match the location's pressure (hPa) at sea level - self.sensor.sea_level_pressure = 1013.25 - return - - def read(self): - #Read the sensor(s), parse the data and store it in redis if redis is configured - - temperature = round((self.sensor.temperature - 5) * 1.8 + 32, 2) - gas = self.sensor.gas - humidity = round(self.sensor.humidity, 1) - pressure = round(self.sensor.pressure, 2) - altitude = round(self.sensor.altitude, 3) - - if humidity is not None and temperature is not None: - variables.r.set(self.key + '_temperature', temperature) - variables.r.set(self.key + '_humidity', humidity) - variables.r.set(self.key + '_gas', gas) - variables.r.set(self.key + '_pressure', pressure) - variables.r.set(self.key + '_altitude', altitude) - readings = {'temperature': temperature, 'humidity': humidity, 'pressure': pressure, 'gas': gas, 'altitude': altitude} - variables.r.set(self.key, json.dumps(readings)) - print('BME680:', readings) - return readings - else: - print('Failed to get reading [BME680]. Try again!') - - - def readRaw(self): - #Read the sensor(s) but return the raw data, useful for debugging - return self.read() diff --git a/sensors/pi/i2c/bme680_sensor.py b/sensors/pi/i2c/bme680_sensor.py index 12245b6..474fcd6 100644 --- a/sensors/pi/i2c/bme680_sensor.py +++ b/sensors/pi/i2c/bme680_sensor.py @@ -2,7 +2,6 @@ import json import redis from .sensor import Sensor -import RPi.GPIO as GPIO import board from busio import I2C import adafruit_bme680 @@ -12,8 +11,6 @@ import variables -#r = redis.Redis(host='127.0.0.1', port=6379) -#PIN MODE : OUT | IN class Bme680Sensor(Sensor): @@ -22,15 +19,12 @@ def __init__(self, address = None, name='PressureSensor', key=None): return def init_sensor(self): - #Initialize the sensor here (i.e. set pin mode, get addresses, etc) this gets called by the worker self.sensor = adafruit_bme680.Adafruit_BME680_I2C(self.i2c, debug=False) # change this to match the location's pressure (hPa) at sea level self.sensor.sea_level_pressure = 1013.25 return def read(self): - #Read the sensor(s), parse the data and store it in redis if redis is configured - temperature = round((self.sensor.temperature - 5) * 1.8 + 32, 2) gas = self.sensor.gas humidity = round(self.sensor.humidity, 1) @@ -50,7 +44,6 @@ def read(self): else: print('Failed to get reading [BME680]. Try again!') - def readRaw(self): #Read the sensor(s) but return the raw data, useful for debugging return self.read() diff --git a/workers/pi_i2c_worker.py b/workers/pi_i2c_worker.py index babcd8c..96341fd 100644 --- a/workers/pi_i2c_worker.py +++ b/workers/pi_i2c_worker.py @@ -5,6 +5,7 @@ import threading import sys sys.path.append('..') +from .worker import Worker from sensors.pi.i2c.bme680_sensor import (Bme680Sensor) import variables @@ -12,29 +13,17 @@ #r = redis.Redis(host='127.0.0.1', port=6379) # def clamp(n, smallest, largest): return max(smallest, min(n, largest)) -class PiI2CWorker(): +class PiI2CWorker(Worker): def __init__(self, config, main_thread_running, system_ready): - #self.config = {**config, **self.config} - self.config = config + super().__init__(config, main_thread_running, system_ready) self.channel = config.get('channel', 'i2c').replace(" ", "_").lower() self.sleep_duration = config.get('sleep_duration', 30) - self.main_thread_running = main_thread_running - self.system_ready = system_ready + self.sensors = [] - self.init_sensors() + self.init() return - def dynamic_import(self, name): - #Split path of the class folder structure: {sensor name}_sensor . {SensorName}Sensor - components = name.split('.') - #Dynamically import root of component path - module = __import__(components[0]) - #Get component attributes - for component in components[1:]: - module = getattr(module, component) - return module - - def init_sensors(self): + def init(self): for sensor in self.config['sensors']: if sensor.get('type', None) is not None: #Get the sensor from the sensors folder {sensor name}_sensor.{SensorName}Sensor @@ -49,7 +38,7 @@ def init_sensors(self): 'key' : sensor.get('key', None) } - # optional sensor variables + # Optional sensor variables # Model is specific to DHT modules to specify DHT11 DHT22 or DHT2302 if sensor.get('model'): sensor_kwargs['model'] = str(sensor.get('model')) @@ -65,15 +54,13 @@ def init_sensors(self): return def run(self): - t = threading.Thread(target=self.work, args=()) - t.start() print('Pi I2C Sensor Worker [' + str(len(self.sensors)) + ' Sensors]...\t\033[1;32m Online\033[0;0m') - return t + return super().run() def work(self): - while self.main_thread_running.is_set(): if self.system_ready.is_set(): + message = {'event':'PiSensorUpdate'} readings = {} @@ -83,7 +70,7 @@ def work(self): variables.r.set(sensor.key, json.dumps(result)) message['data'] = readings - print("Reaadings I2C: ", readings); + print(readings); variables.r.publish(self.channel, json.dumps(message)) time.sleep(self.sleep_duration) diff --git a/workers/pi_lcd_worker.py b/workers/pi_lcd_worker.py new file mode 100644 index 0000000..b19711a --- /dev/null +++ b/workers/pi_lcd_worker.py @@ -0,0 +1,137 @@ +import time +import datetime +import json +import redis +import threading +import board +import busio +import adafruit_character_lcd.character_lcd_rgb_i2c as character_rgb_lcd +import adafruit_character_lcd.character_lcd_i2c as character_lcd +import sys +sys.path.append('..') + +import variables + +#r = redis.Redis(host='127.0.0.1', port=6379) + + +class LcdWorker(): + def __init__(self, config, main_thread_running, system_ready, lcd_available): + self.config = config + self.address = str(self.config['address']) if self.config['address'] is not None else None + self.model = str(self.config['model']) if self.config['model'] is not None else None + self.columns = int(self.config['columns']) if self.config['columns'] is not None else 16 + self.rows = int(self.config['rows']) if self.config['rows'] is not None else 2 + + #Events + self.main_thread_running = main_thread_running + self.system_ready = system_ready + self.lcd_available = lcd_available + + #Dynamic Properties based on config + self.topic = self.config['topic'].replace(" ", "/").lower() if self.config['topic'] is not None else 'mudpi/lcd/' + self.message_queue = [] + + #Pubsub Listeners + self.pubsub = variables.r.pubsub() + self.pubsub.subscribe(**{self.topic: self.handleMessage}) + + self.init() + return + + def init(self): + # prepare sensor on specified pin + # + if (self.model.lower() == 'rgb'): + self.lcd = character_lcd.Character_LCD_RGB_I2C(self.i2c, self.columns, self.rows, self.address) + else: + self.lcd = character_lcd.Character_LCD_I2C(self.i2c, self.columns, self.rows, self.address) + + self.lcd.message = "MudPi\nGarden Online" + print('LCD Worker...\t\t\t\033[1;32m Initialized\033[0;0m'.format(**self.config)) + return + + def run(self): + t = threading.Thread(target=self.work, args=()) + t.start() + print('LCD Worker ...\t\t\t\033[1;32m Online\033[0;0m'.format(**self.config)) + return t + + def decodeMessageData(self, message): + if isinstance(message, dict): + #print('Dict Found') + return message + elif isinstance(message.decode('utf-8'), str): + try: + temp = json.loads(message.decode('utf-8')) + #print('Json Found') + return temp + except: + #print('Json Error. Str Found') + return {'event':'Unknown', 'data':message} + else: + #print('Failed to detect type') + return {'event':'Unknown', 'data':message} + + def handleMessage(self, message): + data = message['data'] + if data is not None: + decoded_message = self.decodeMessageData(data) + try: + if decoded_message['event'] == 'Message': + if decoded_message.get('data', None): + self.lcd.message = decoded_message.get('data', '') + elif decoded_message.get('data', None) == 0: + self.lcd.clear() + print('LCD Message to \033[1;36m{0}\033[0;0m'.format(decoded_message['data'])) + elif decoded_message['event'] == 'Clear': + self.lcd.clear() + print('Cleared the LCD Screen') + except: + print('Error Decoding Message for LCD') + + def elapsedTime(self): + self.time_elapsed = time.perf_counter() - self.time_start + return self.time_elapsed + + def resetElapsedTime(self): + self.time_start = time.perf_counter() + pass + + def addMessageToQueue(self, message): + #Add message to queue if LCD available + if self.lcd_available.is_set(): + if not self.active: + message = {'event':'StateChanged', 'data':1} + variables.r.set(self.config['key']+'_state', 1) + variables.r.publish(self.topic, json.dumps(message)) + self.active = True + #self.relay_active.set() This is handled by the redis listener now + self.resetElapsedTime() + + def work(self): + self.resetElapsedTime() + while self.main_thread_running.is_set(): + if self.system_ready.is_set(): + + try: + self.pubsub.get_message() + if self.lcd_available.is_set(): + pass + else: + time.sleep(1) + except: + print("LCD Worker \t\033[1;31m Unexpected Error\033[0;0m".format(**self.config)) + + else: + #System not ready + time.sleep(1) + self.resetElapsedTime() + + time.sleep(0.1) + + + #This is only ran after the main thread is shut down + #Close the pubsub connection + self.pubsub.close() + print("LCD Worker Shutting Down...\t\033[1;32m Complete\033[0;0m".format(**self.config)) \ No newline at end of file diff --git a/workers/relay_worker.py b/workers/relay_worker.py index 9e2a602..f464466 100644 --- a/workers/relay_worker.py +++ b/workers/relay_worker.py @@ -10,7 +10,7 @@ import variables #r = redis.Redis(host='127.0.0.1', port=6379) -GPIO.setmode(GPIO.BCM) +# GPIO.setmode(GPIO.BCM) # ToDO Update relay to make a key if one is not set in config diff --git a/workers/worker.py b/workers/worker.py new file mode 100644 index 0000000..36c6903 --- /dev/null +++ b/workers/worker.py @@ -0,0 +1,74 @@ +import time +import datetime +import json +import redis +import threading +import sys +sys.path.append('..') + +import variables + +def log(func): + def wrapper(*args, **kwargs): + print("MudPi Debug Log: " + " ".join([str(arg) for arg in args]) + " at " + str(datetime.datetime.now())) + value = func(*args, **kwargs) + return value + +# Base Worker Class +# A worker is responsible for handling its set of operations and running on a thread +class Worker(): + def __init__(self, config, main_thread_running, system_ready): + self.config = config + self.channel = config.get('channel', 'mudpi').replace(" ", "_").lower() + self.sleep_duration = config.get('sleep_duration', 15) + + # Threading Events to Keep Everything in Sync + self.main_thread_running = main_thread_running + self.system_ready = system_ready + self.worker_available = threading.Event() + + self.components = [] + self.init() + return + + def init(self): + # print('Worker...\t\t\t\033[1;32m Initializing\033[0;0m'.format(**control)) + return + + def run(self): + t = threading.Thread(target=self.work, args=()) + t.start() + return t + + def work(self): + while self.main_thread_running.is_set(): + if self.system_ready.is_set(): + time.sleep(self.sleep_duration) + #This is only ran after the main thread is shut down + print("Worker Shutting Down...\t\033[1;32m Complete\033[0;0m") + + def dynamic_import(self, name): + # Split path of the class folder structure: {sensor name}_sensor . {SensorName}Sensor + components = name.split('.') + # Dynamically import root of component path + module = __import__(components[0]) + # Get component attributes + for component in components[1:]: + module = getattr(module, component) + return module + + def decodeMessageData(self, message): + if isinstance(message, dict): + #print('Dict Found') + return message + elif isinstance(message.decode('utf-8'), str): + try: + temp = json.loads(message.decode('utf-8')) + #print('Json Found') + return temp + except: + #print('Json Error. Str Found') + return {'event':'Unknown', 'data':message} + else: + #print('Failed to detect type') + return {'event':'Unknown', 'data':message} \ No newline at end of file From a39d2b42bec10ed6a73e6a3e1c97544ee4ade19c Mon Sep 17 00:00:00 2001 From: Eric Davisson Date: Thu, 13 Aug 2020 20:05:46 -0500 Subject: [PATCH 54/97] More refactoring --- mudpi.py | 1 - workers/lcd_worker.py | 207 ----------------------------------------- workers/pump_worker.py | 109 ---------------------- workers/worker.py | 2 +- 4 files changed, 1 insertion(+), 318 deletions(-) delete mode 100644 workers/lcd_worker.py delete mode 100644 workers/pump_worker.py diff --git a/mudpi.py b/mudpi.py index c6d105d..a3d0416 100755 --- a/mudpi.py +++ b/mudpi.py @@ -159,7 +159,6 @@ except KeyError: print('MudPi Triggers...\t\t\t\033[1;31m Disabled\033[0;0m') - # Worker for nodes attached to pi via serial or wifi[esp8266, esp32] # Supported nodes: arduinos, esp8266, ADC-MCP3xxx, probably others (esp32 with custom nanpy fork) try: diff --git a/workers/lcd_worker.py b/workers/lcd_worker.py deleted file mode 100644 index e216b5b..0000000 --- a/workers/lcd_worker.py +++ /dev/null @@ -1,207 +0,0 @@ - - -# The wiring for the LCD is as follows: -# 1 : GND -# 2 : 5V -# 3 : Contrast (0-5V)* -# 4 : RS (Register Select) -# 5 : R/W (Read Write) - GROUND THIS PIN -# 6 : Enable or Strobe -# 7 : Data Bit 0 - NOT USED -# 8 : Data Bit 1 - NOT USED -# 9 : Data Bit 2 - NOT USED -# 10: Data Bit 3 - NOT USED -# 11: Data Bit 4 -# 12: Data Bit 5 -# 13: Data Bit 6 -# 14: Data Bit 7 -# 15: LCD Backlight +5V** -# 16: LCD Backlight GND - -import RPi.GPIO as GPIO -import time -import redis -import json -import threading -import logging - -import sys -sys.path.append('..') -import variables - -# Define GPIO to LCD mapping -LCD_RS = 7 -LCD_E = 8 -LCD_D4 = 25 -LCD_D5 = 24 -LCD_D6 = 23 -LCD_D7 = 18 - -# Define some device constants -LCD_WIDTH = 16 # Maximum characters per line -LCD_CHR = True -LCD_CMD = False - -LCD_LINE_1 = 0x80 # LCD RAM address for the 1st line -LCD_LINE_2 = 0xC0 # LCD RAM address for the 2nd line - -# Timing constants -E_PULSE = 0.0005 -E_DELAY = 0.0005 - -MESSAGE_QUEUE = [] - -#r = redis.Redis(host='127.0.0.1', port=6379) -r = variables.r - -class LCDWorker(): - - def __init__(self, new_messages_waiting, main_thread_running, system_ready): - self.new_messages_waiting = new_messages_waiting - self.main_thread_running = main_thread_running - self.system_ready = system_ready - return - - def run(self): - t = threading.Thread(target=self.process_loop, args=()) - t.start() - print('LCD Worker...\t\t\t\t\033[1;32m Online\033[0;0m') - return t - - def process_loop(self): - self.prepare_gpio() - self.prepare_messages() - - try: - while self.main_thread_running.is_set(): - if(self.system_ready.is_set()): - global MESSAGE_QUEUE - - #print('Message Queue Begin:') - for msg in MESSAGE_QUEUE: - if not (self.main_thread_running.is_set()): - return - - #print('LCD MESSAGE\nLine 1: %s \nLine 2: %s' % (msg['line_1'],msg['line_2'])) - # Send some test - self.lcd_string(msg['line_1'],LCD_LINE_1) - self.lcd_string(msg['line_2'],LCD_LINE_2) - - time.sleep(3) # 3 second delay - - #Display Control Messages - #print('Main Messages Begin:') - self.lcd_string(variables.lcd_message['line_1'],LCD_LINE_1) - self.lcd_string(variables.lcd_message['line_2'],LCD_LINE_2) - time.sleep(3) - - if ((not MESSAGE_QUEUE) or (self.new_messages_waiting.is_set()) and (self.main_thread_running.is_set())): - #print('|| LCD Loading New Messages ||') - time.sleep(5) - self.prepare_messages() - #Clear the event that tells us we should download messages - self.new_messages_waiting.clear() - - #This is after the main thread has ended, clear out the lcd display - print('LCD Worker Shutting Down...\t\t\033[1;32m Complete\033[0;0m') - self.lcd_byte(0x01, LCD_CMD) - self.lcd_string("Garden Control",LCD_LINE_1) - self.lcd_string("Shutting Down...",LCD_LINE_2) - GPIO.cleanup() - except KeyboardInterrupt: - self.lcd_byte(0x01, LCD_CMD) - self.lcd_string("Garden Control",LCD_LINE_1) - self.lcd_string("Shutting Down...",LCD_LINE_2) - GPIO.cleanup() - - def prepare_messages(self): - global MESSAGE_QUEUE - if r.exists('lcdmessages'): - MESSAGE_QUEUE = json.loads(r.get('lcdmessages').decode('utf-8')) - #print('Message Pulled:', MESSAGE_QUEUE) - - - def prepare_gpio(self): - # Main program block - GPIO.setwarnings(False) - GPIO.setmode(GPIO.BCM) # Use BCM GPIO numbers - GPIO.setup(LCD_E, GPIO.OUT) # E - GPIO.setup(LCD_RS, GPIO.OUT) # RS - GPIO.setup(LCD_D4, GPIO.OUT) # DB4 - GPIO.setup(LCD_D5, GPIO.OUT) # DB5 - GPIO.setup(LCD_D6, GPIO.OUT) # DB6 - GPIO.setup(LCD_D7, GPIO.OUT) # DB7 - # Initialise display - self.lcd_init() - - - def lcd_init(self): - # Initialise display - self.lcd_byte(0x33,LCD_CMD) # 110011 Initialise - self.lcd_byte(0x32,LCD_CMD) # 110010 Initialise - self.lcd_byte(0x06,LCD_CMD) # 000110 Cursor move direction - self.lcd_byte(0x0C,LCD_CMD) # 001100 Display On,Cursor Off, Blink Off - self.lcd_byte(0x28,LCD_CMD) # 101000 Data length, number of lines, font size - self.lcd_byte(0x01,LCD_CMD) # 000001 Clear display - time.sleep(E_DELAY) - - def lcd_byte(self, bits, mode): - # Send byte to data pins - # bits = data - # mode = True for character - # False for command - - GPIO.output(LCD_RS, mode) # RS - - # High bits - GPIO.output(LCD_D4, False) - GPIO.output(LCD_D5, False) - GPIO.output(LCD_D6, False) - GPIO.output(LCD_D7, False) - if bits&0x10==0x10: - GPIO.output(LCD_D4, True) - if bits&0x20==0x20: - GPIO.output(LCD_D5, True) - if bits&0x40==0x40: - GPIO.output(LCD_D6, True) - if bits&0x80==0x80: - GPIO.output(LCD_D7, True) - - # Toggle 'Enable' pin - self.lcd_toggle_enable() - - # Low bits - GPIO.output(LCD_D4, False) - GPIO.output(LCD_D5, False) - GPIO.output(LCD_D6, False) - GPIO.output(LCD_D7, False) - if bits&0x01==0x01: - GPIO.output(LCD_D4, True) - if bits&0x02==0x02: - GPIO.output(LCD_D5, True) - if bits&0x04==0x04: - GPIO.output(LCD_D6, True) - if bits&0x08==0x08: - GPIO.output(LCD_D7, True) - - # Toggle 'Enable' pin - self.lcd_toggle_enable() - - def lcd_toggle_enable(self): - # Toggle enable - time.sleep(E_DELAY) - GPIO.output(LCD_E, True) - time.sleep(E_PULSE) - GPIO.output(LCD_E, False) - time.sleep(E_DELAY) - - def lcd_string(self,message,line): - # Send string to display - - message = message.ljust(LCD_WIDTH," ") - - self.lcd_byte(line, LCD_CMD) - - for i in range(LCD_WIDTH): - self.lcd_byte(ord(message[i]),LCD_CHR) - \ No newline at end of file diff --git a/workers/pump_worker.py b/workers/pump_worker.py deleted file mode 100644 index 0672f70..0000000 --- a/workers/pump_worker.py +++ /dev/null @@ -1,109 +0,0 @@ -import time -import datetime -import json -import redis -import threading -import sys -import RPi.GPIO as GPIO -sys.path.append('..') - -import variables - -#r = redis.Redis(host='127.0.0.1', port=6379) -GPIO.setmode(GPIO.BCM) - -class PumpWorker(): - def __init__(self, config, main_thread_running, system_ready, pump_ready, pump_should_be_running): - #self.config = {**config, **self.config} - self.config = config - self.config['pin'] = int(self.config['pin']) #parse possbile strings to avoid errors - self.main_thread_running = main_thread_running - self.system_ready = system_ready - self.pump_ready = pump_ready - self.pump_should_be_running = pump_should_be_running - self.pump_running = False - self.needs_first_water_cycle = True - self.init() - return - - def init(self): - GPIO.setup(self.config['pin'], GPIO.OUT) - #Close the relay by default - GPIO.output(self.config['pin'], GPIO.HIGH) - print('Pump Worker...\t\t\t\t\033[1;32m Ready\033[0;0m') - return - - def run(self): - t = threading.Thread(target=self.work, args=()) - t.start() - print('Pump Worker...\t\t\t\t\033[1;32m Online\033[0;0m') - return t - - def elapsedTime(self): - self.time_elapsed = time.perf_counter() - self.time_start - return self.time_elapsed - - def resetElapsedTime(self): - self.time_start = time.perf_counter() - pass - - def checkFirstWaterCycle(self): - if self.needs_first_water_cycle: - self.turnPumpOn() - self.needs_first_water_cycle = False - else: - if self.pump_ready.is_set(): - if self.pump_should_be_running.is_set() and not self.pump_running: - self.needs_first_water_cycle = True - else: - self.turnPumpOff() - - def turnPumpOn(self): - #Turn off voltage to flip on relay - if not self.pump_running: - message = {'event':'PumpTurnedOn', 'data':1} - GPIO.output(self.config['pin'], GPIO.LOW) - variables.r.set('pump_running', True) - variables.r.publish('pump', json.dumps(message)) - variables.r.set('last_watered_at', datetime.datetime.now()) #Store current time to track watering times - self.pump_running = True - self.resetElapsedTime() - print('Pump Turning On!') - - def turnPumpOff(self): - #Turn off voltage to flip on relay - if self.pump_running: - message = {'event':'PumpTurnedOff', 'data':1} - GPIO.output(self.config['pin'], GPIO.HIGH) - self.pump_should_be_running.clear() - variables.r.delete('pump_running', False) - variables.r.publish('pump', json.dumps(message)) - self.pump_running = False - print('Pump Turning Off!') - - def work(self): - self.resetElapsedTime() - while self.main_thread_running.is_set(): - if self.system_ready.is_set(): - while self.pump_should_be_running.is_set() and self.pump_ready.is_set(): - - self.checkFirstWaterCycle() - - #Calculate elapsed time and check against system limit - if (self.elapsedTime() >= self.config['max_duration']): - self.turnPumpOff() - - time.sleep(1) - #Waiting for next pump cycle - self.turnPumpOff() - time.sleep(5) - self.resetElapsedTime() - else: - #System not ready pump should be off - self.turnPumpOff() - time.sleep(5) - self.resetElapsedTime() - - time.sleep(5) - #This is only ran after the main thread is shut down - print("Pump Worker Shutting Down...\t\t\033[1;32m Complete\033[0;0m") \ No newline at end of file diff --git a/workers/worker.py b/workers/worker.py index 36c6903..116295b 100644 --- a/workers/worker.py +++ b/workers/worker.py @@ -43,7 +43,7 @@ def run(self): def work(self): while self.main_thread_running.is_set(): if self.system_ready.is_set(): - time.sleep(self.sleep_duration) + time.sleep(self.sleep_duration) #This is only ran after the main thread is shut down print("Worker Shutting Down...\t\033[1;32m Complete\033[0;0m") From fb73cdaaeb474513eba7c1c65c2decfa8ae4bc60 Mon Sep 17 00:00:00 2001 From: Eric Davisson Date: Thu, 13 Aug 2020 20:06:34 -0500 Subject: [PATCH 55/97] Remove old LCD worker --- mudpi.py | 1 - 1 file changed, 1 deletion(-) diff --git a/mudpi.py b/mudpi.py index a3d0416..662a6f6 100755 --- a/mudpi.py +++ b/mudpi.py @@ -9,7 +9,6 @@ from action import Action from config_load import loadConfigJson from server.mudpi_server import MudpiServer -from workers.lcd_worker import LCDWorker from workers.relay_worker import RelayWorker from workers.camera_worker import CameraWorker from workers.trigger_worker import TriggerWorker From 0444e33808f30377c7d6578be3f79c3ac636d02d Mon Sep 17 00:00:00 2001 From: Eric Davisson Date: Thu, 13 Aug 2020 20:08:58 -0500 Subject: [PATCH 56/97] Updated base Worker --- workers/worker.py | 1 - 1 file changed, 1 deletion(-) diff --git a/workers/worker.py b/workers/worker.py index 116295b..c3d009d 100644 --- a/workers/worker.py +++ b/workers/worker.py @@ -28,7 +28,6 @@ def __init__(self, config, main_thread_running, system_ready): self.worker_available = threading.Event() self.components = [] - self.init() return def init(self): From 13104b5d992d955976116e4cc6346b1d0cd2cca3 Mon Sep 17 00:00:00 2001 From: Eric Davisson Date: Thu, 13 Aug 2020 20:35:48 -0500 Subject: [PATCH 57/97] Refactoring Workers --- mudpi.py | 12 +- variables.py | 2 +- workers/arduino/__init__.py | 0 .../{ => arduino}/arduino_control_worker.py | 0 workers/{ => arduino}/arduino_relay_worker.py | 0 .../{ => arduino}/arduino_sensor_worker.py | 0 workers/{ => arduino}/arduino_worker.py | 0 workers/arduino/worker.py | 73 ++++++++++ workers/pi/__init__.py | 0 workers/{ => pi}/camera_worker.py | 24 +--- .../control_worker.py} | 32 +---- .../{pi_i2c_worker.py => pi/i2c_worker.py} | 0 .../{pi_lcd_worker.py => pi/lcd_worker.py} | 40 +----- workers/{ => pi}/relay_worker.py | 48 +------ .../sensor_worker.py} | 34 ++--- workers/pi/worker.py | 73 ++++++++++ workers/sensor_worker.py | 127 ------------------ workers/trigger_worker.py | 33 ++--- workers/worker.py | 8 ++ 19 files changed, 201 insertions(+), 305 deletions(-) create mode 100644 workers/arduino/__init__.py rename workers/{ => arduino}/arduino_control_worker.py (100%) rename workers/{ => arduino}/arduino_relay_worker.py (100%) rename workers/{ => arduino}/arduino_sensor_worker.py (100%) rename workers/{ => arduino}/arduino_worker.py (100%) create mode 100644 workers/arduino/worker.py create mode 100644 workers/pi/__init__.py rename workers/{ => pi}/camera_worker.py (90%) rename workers/{pi_control_worker.py => pi/control_worker.py} (73%) rename workers/{pi_i2c_worker.py => pi/i2c_worker.py} (100%) rename workers/{pi_lcd_worker.py => pi/lcd_worker.py} (78%) rename workers/{ => pi}/relay_worker.py (80%) rename workers/{pi_sensor_worker.py => pi/sensor_worker.py} (76%) create mode 100644 workers/pi/worker.py delete mode 100644 workers/sensor_worker.py diff --git a/mudpi.py b/mudpi.py index 662a6f6..878c723 100755 --- a/mudpi.py +++ b/mudpi.py @@ -9,14 +9,14 @@ from action import Action from config_load import loadConfigJson from server.mudpi_server import MudpiServer -from workers.relay_worker import RelayWorker -from workers.camera_worker import CameraWorker +from workers.pi.i2c_worker import PiI2CWorker +from workers.pi.relay_worker import RelayWorker +from workers.pi.camera_worker import CameraWorker +from workers.pi.sensor_worker import PiSensorWorker +from workers.pi.control_worker import PiControlWorker from workers.trigger_worker import TriggerWorker -from workers.pi_sensor_worker import PiSensorWorker -from workers.pi_control_worker import PiControlWorker -from workers.pi_i2c_worker import PiI2CWorker try: - from workers.arduino_worker import ArduinoWorker + from workers.arduino.arduino_worker import ArduinoWorker NANPY_ENABLED = True except ImportError: NANPY_ENABLED = False diff --git a/variables.py b/variables.py index 3d2bc3f..28bf8ff 100644 --- a/variables.py +++ b/variables.py @@ -7,5 +7,5 @@ YELLOW_BACK="\x1b[43;30m" RESET="\x1b[0m" -#Singleton redis to prevent connection conflicts +# Singleton redis to prevent connection conflicts r = redis.Redis(host='127.0.0.1', port=6379) \ No newline at end of file diff --git a/workers/arduino/__init__.py b/workers/arduino/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/workers/arduino_control_worker.py b/workers/arduino/arduino_control_worker.py similarity index 100% rename from workers/arduino_control_worker.py rename to workers/arduino/arduino_control_worker.py diff --git a/workers/arduino_relay_worker.py b/workers/arduino/arduino_relay_worker.py similarity index 100% rename from workers/arduino_relay_worker.py rename to workers/arduino/arduino_relay_worker.py diff --git a/workers/arduino_sensor_worker.py b/workers/arduino/arduino_sensor_worker.py similarity index 100% rename from workers/arduino_sensor_worker.py rename to workers/arduino/arduino_sensor_worker.py diff --git a/workers/arduino_worker.py b/workers/arduino/arduino_worker.py similarity index 100% rename from workers/arduino_worker.py rename to workers/arduino/arduino_worker.py diff --git a/workers/arduino/worker.py b/workers/arduino/worker.py new file mode 100644 index 0000000..c3d009d --- /dev/null +++ b/workers/arduino/worker.py @@ -0,0 +1,73 @@ +import time +import datetime +import json +import redis +import threading +import sys +sys.path.append('..') + +import variables + +def log(func): + def wrapper(*args, **kwargs): + print("MudPi Debug Log: " + " ".join([str(arg) for arg in args]) + " at " + str(datetime.datetime.now())) + value = func(*args, **kwargs) + return value + +# Base Worker Class +# A worker is responsible for handling its set of operations and running on a thread +class Worker(): + def __init__(self, config, main_thread_running, system_ready): + self.config = config + self.channel = config.get('channel', 'mudpi').replace(" ", "_").lower() + self.sleep_duration = config.get('sleep_duration', 15) + + # Threading Events to Keep Everything in Sync + self.main_thread_running = main_thread_running + self.system_ready = system_ready + self.worker_available = threading.Event() + + self.components = [] + return + + def init(self): + # print('Worker...\t\t\t\033[1;32m Initializing\033[0;0m'.format(**control)) + return + + def run(self): + t = threading.Thread(target=self.work, args=()) + t.start() + return t + + def work(self): + while self.main_thread_running.is_set(): + if self.system_ready.is_set(): + time.sleep(self.sleep_duration) + #This is only ran after the main thread is shut down + print("Worker Shutting Down...\t\033[1;32m Complete\033[0;0m") + + def dynamic_import(self, name): + # Split path of the class folder structure: {sensor name}_sensor . {SensorName}Sensor + components = name.split('.') + # Dynamically import root of component path + module = __import__(components[0]) + # Get component attributes + for component in components[1:]: + module = getattr(module, component) + return module + + def decodeMessageData(self, message): + if isinstance(message, dict): + #print('Dict Found') + return message + elif isinstance(message.decode('utf-8'), str): + try: + temp = json.loads(message.decode('utf-8')) + #print('Json Found') + return temp + except: + #print('Json Error. Str Found') + return {'event':'Unknown', 'data':message} + else: + #print('Failed to detect type') + return {'event':'Unknown', 'data':message} \ No newline at end of file diff --git a/workers/pi/__init__.py b/workers/pi/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/workers/camera_worker.py b/workers/pi/camera_worker.py similarity index 90% rename from workers/camera_worker.py rename to workers/pi/camera_worker.py index f49152e..ce77c4a 100644 --- a/workers/camera_worker.py +++ b/workers/pi/camera_worker.py @@ -7,25 +7,21 @@ import os import RPi.GPIO as GPIO from picamera import PiCamera +from .worker import Worker sys.path.append('..') import variables -#r = redis.Redis(host='127.0.0.1', port=6379) -GPIO.setmode(GPIO.BCM) -class CameraWorker(): +class CameraWorker(Worker): def __init__(self, config, main_thread_running, system_ready, camera_available): - #self.config = {**config, **self.config} - self.config = config + super().__init__(config, main_thread_running, system_ready) self.pending_reset = False - #Events - self.main_thread_running = main_thread_running - self.system_ready = system_ready + # Events self.camera_available = camera_available - #Dynamic Properties based on config + # Dynamic Properties based on config self.path = self.config['path'].replace(" ", "-") if self.config['path'] is not None else '/etc/mudpi/img/' self.topic = self.config['topic'].replace(" ", "/").lower() if self.config['topic'] is not None else 'mudpi/camera/' if self.config['resolution'] is not None: @@ -75,19 +71,11 @@ def wait(self): try: self.next_time = (datetime.datetime.now() + datetime.timedelta(hours=self.hours, minutes=self.minutes, seconds=self.seconds)).replace(microsecond=0) except: - #Default every hour + # Default every hour self.next_time = (datetime.datetime.now() + datetime.timedelta(hours=1)).replace(minute=0, second=0, microsecond=0) delay = (self.next_time - datetime.datetime.now()).seconds time.sleep(delay) - def elapsedTime(self): - self.time_elapsed = time.perf_counter() - self.time_start - return self.time_elapsed - - def resetElapsedTime(self): - self.time_start = time.perf_counter() - pass - def handleEvent(self, message): data = message['data'] decoded_message = None diff --git a/workers/pi_control_worker.py b/workers/pi/control_worker.py similarity index 73% rename from workers/pi_control_worker.py rename to workers/pi/control_worker.py index ed1b5de..d254d9e 100644 --- a/workers/pi_control_worker.py +++ b/workers/pi/control_worker.py @@ -3,6 +3,7 @@ import json import redis import threading +from .worker import Worker import sys sys.path.append('..') from controls.pi.button_control import (ButtonControl) @@ -10,33 +11,17 @@ import variables -#r = redis.Redis(host='127.0.0.1', port=6379) -# def clamp(n, smallest, largest): return max(smallest, min(n, largest)) - -class PiControlWorker(): +class PiControlWorker(Worker): def __init__(self, config, main_thread_running, system_ready): - #self.config = {**config, **self.config} - self.config = config + super().__init__(config, main_thread_running, system_ready) self.channel = config.get('channel', 'controls').replace(" ", "_").lower() self.sleep_duration = config.get('sleep_duration', 0.5) - self.main_thread_running = main_thread_running - self.system_ready = system_ready - #Store pump event so we can shutdown pump with float readings + self.controls = [] - self.init_controls() + self.init() return - def dynamic_import(self, name): - #Split path of the class folder structure: {sensor name}_sensor . {SensorName}Sensor - components = name.split('.') - #Dynamically import root of component path - module = __import__(components[0]) - #Get component attributes - for component in components[1:]: - module = getattr(module, component) - return module - - def init_controls(self): + def init(self): for control in self.config['controls']: if control.get('type', None) is not None: #Get the control from the controls folder {control name}_control.{ControlName}Control @@ -67,13 +52,10 @@ def init_controls(self): return def run(self): - t = threading.Thread(target=self.work, args=()) - t.start() print('Pi Control Worker [' + str(len(self.config['controls'])) + ' Controls]...\t\033[1;32m Online\033[0;0m') - return t + return super().run() def work(self): - while self.main_thread_running.is_set(): if self.system_ready.is_set(): readings = {} diff --git a/workers/pi_i2c_worker.py b/workers/pi/i2c_worker.py similarity index 100% rename from workers/pi_i2c_worker.py rename to workers/pi/i2c_worker.py diff --git a/workers/pi_lcd_worker.py b/workers/pi/lcd_worker.py similarity index 78% rename from workers/pi_lcd_worker.py rename to workers/pi/lcd_worker.py index b19711a..70acf63 100644 --- a/workers/pi_lcd_worker.py +++ b/workers/pi/lcd_worker.py @@ -7,25 +7,21 @@ import busio import adafruit_character_lcd.character_lcd_rgb_i2c as character_rgb_lcd import adafruit_character_lcd.character_lcd_i2c as character_lcd +from .worker import Worker import sys sys.path.append('..') import variables -#r = redis.Redis(host='127.0.0.1', port=6379) - - -class LcdWorker(): +class LcdWorker(Worker): def __init__(self, config, main_thread_running, system_ready, lcd_available): - self.config = config + super().__init__(config, main_thread_running, system_ready) self.address = str(self.config['address']) if self.config['address'] is not None else None self.model = str(self.config['model']) if self.config['model'] is not None else None self.columns = int(self.config['columns']) if self.config['columns'] is not None else 16 self.rows = int(self.config['rows']) if self.config['rows'] is not None else 2 #Events - self.main_thread_running = main_thread_running - self.system_ready = system_ready self.lcd_available = lcd_available #Dynamic Properties based on config @@ -41,7 +37,6 @@ def __init__(self, config, main_thread_running, system_ready, lcd_available): def init(self): # prepare sensor on specified pin - # if (self.model.lower() == 'rgb'): self.lcd = character_lcd.Character_LCD_RGB_I2C(self.i2c, self.columns, self.rows, self.address) else: @@ -52,26 +47,8 @@ def init(self): return def run(self): - t = threading.Thread(target=self.work, args=()) - t.start() print('LCD Worker ...\t\t\t\033[1;32m Online\033[0;0m'.format(**self.config)) - return t - - def decodeMessageData(self, message): - if isinstance(message, dict): - #print('Dict Found') - return message - elif isinstance(message.decode('utf-8'), str): - try: - temp = json.loads(message.decode('utf-8')) - #print('Json Found') - return temp - except: - #print('Json Error. Str Found') - return {'event':'Unknown', 'data':message} - else: - #print('Failed to detect type') - return {'event':'Unknown', 'data':message} + return super().run() def handleMessage(self, message): data = message['data'] @@ -89,14 +66,6 @@ def handleMessage(self, message): print('Cleared the LCD Screen') except: print('Error Decoding Message for LCD') - - def elapsedTime(self): - self.time_elapsed = time.perf_counter() - self.time_start - return self.time_elapsed - - def resetElapsedTime(self): - self.time_start = time.perf_counter() - pass def addMessageToQueue(self, message): #Add message to queue if LCD available @@ -130,7 +99,6 @@ def work(self): time.sleep(0.1) - #This is only ran after the main thread is shut down #Close the pubsub connection self.pubsub.close() diff --git a/workers/relay_worker.py b/workers/pi/relay_worker.py similarity index 80% rename from workers/relay_worker.py rename to workers/pi/relay_worker.py index f464466..c16aafa 100644 --- a/workers/relay_worker.py +++ b/workers/pi/relay_worker.py @@ -5,34 +5,27 @@ import threading import sys import RPi.GPIO as GPIO +from .worker import Worker sys.path.append('..') import variables -#r = redis.Redis(host='127.0.0.1', port=6379) -# GPIO.setmode(GPIO.BCM) - -# ToDO Update relay to make a key if one is not set in config - -class RelayWorker(): +class RelayWorker(Worker): def __init__(self, config, main_thread_running, system_ready, relay_available, relay_active): - #self.config = {**config, **self.config} - self.config = config + super().__init__(config, main_thread_running, system_ready) self.config['pin'] = int(self.config['pin']) #parse possbile strings to avoid errors - #Events - self.main_thread_running = main_thread_running - self.system_ready = system_ready + # Events self.relay_available = relay_available self.relay_active = relay_active - #Dynamic Properties based on config + # Dynamic Properties based on config self.active = False self.topic = self.config['topic'].replace(" ", "/").lower() if self.config['topic'] is not None else 'mudpi/relay/' self.pin_state_off = GPIO.HIGH if self.config['normally_open'] is not None and self.config['normally_open'] else GPIO.LOW self.pin_state_on = GPIO.LOW if self.config['normally_open'] is not None and self.config['normally_open'] else GPIO.HIGH - #Pubsub Listeners + # Pubsub Listeners self.pubsub = variables.r.pubsub() self.pubsub.subscribe(**{self.topic: self.handleMessage}) @@ -56,26 +49,8 @@ def init(self): return def run(self): - t = threading.Thread(target=self.work, args=()) - t.start() print('Relay Worker {key}...\t\t\t\033[1;32m Online\033[0;0m'.format(**self.config)) - return t - - def decodeMessageData(self, message): - if isinstance(message, dict): - #print('Dict Found') - return message - elif isinstance(message.decode('utf-8'), str): - try: - temp = json.loads(message.decode('utf-8')) - #print('Json Found') - return temp - except: - #print('Json Error. Str Found') - return {'event':'Unknown', 'data':message} - else: - #print('Failed to detect type') - return {'event':'Unknown', 'data':message} + return super().run() def handleMessage(self, message): data = message['data'] @@ -97,14 +72,6 @@ def handleMessage(self, message): print('Toggle Relay \033[1;36m{0} {1} \033[0;0m'.format(self.config['key'], state)) except: print('Error Decoding Message for Relay {0}'.format(self.config['key'])) - - def elapsedTime(self): - self.time_elapsed = time.perf_counter() - self.time_start - return self.time_elapsed - - def resetElapsedTime(self): - self.time_start = time.perf_counter() - pass def turnOn(self): #Turn on relay if its available @@ -156,7 +123,6 @@ def work(self): time.sleep(0.1) - #This is only ran after the main thread is shut down #Close the pubsub connection self.pubsub.close() diff --git a/workers/pi_sensor_worker.py b/workers/pi/sensor_worker.py similarity index 76% rename from workers/pi_sensor_worker.py rename to workers/pi/sensor_worker.py index 298c945..ea37ac7 100644 --- a/workers/pi_sensor_worker.py +++ b/workers/pi/sensor_worker.py @@ -3,6 +3,7 @@ import json import redis import threading +from .worker import Worker import sys sys.path.append('..') from sensors.pi.float_sensor import (FloatSensor) @@ -10,33 +11,18 @@ import variables -#r = redis.Redis(host='127.0.0.1', port=6379) -# def clamp(n, smallest, largest): return max(smallest, min(n, largest)) -class PiSensorWorker(): +class PiSensorWorker(Worker): def __init__(self, config, main_thread_running, system_ready): - #self.config = {**config, **self.config} - self.config = config + super().__init__(config, main_thread_running, system_ready) self.channel = config.get('channel', 'sensors').replace(" ", "_").lower() self.sleep_duration = config.get('sleep_duration', 30) - self.main_thread_running = main_thread_running - self.system_ready = system_ready - #Store pump event so we can shutdown pump with float readings + self.sensors = [] - self.init_sensors() + self.init() return - def dynamic_import(self, name): - #Split path of the class folder structure: {sensor name}_sensor . {SensorName}Sensor - components = name.split('.') - #Dynamically import root of component path - module = __import__(components[0]) - #Get component attributes - for component in components[1:]: - module = getattr(module, component) - return module - - def init_sensors(self): + def init(self): for sensor in self.config['sensors']: if sensor.get('type', None) is not None: #Get the sensor from the sensors folder {sensor name}_sensor.{SensorName}Sensor @@ -71,13 +57,10 @@ def init_sensors(self): return def run(self): - t = threading.Thread(target=self.work, args=()) - t.start() print('Pi Sensor Worker [' + str(len(self.sensors)) + ' Sensors]...\t\t\033[1;32m Online\033[0;0m') - return t + return super().run() def work(self): - while self.main_thread_running.is_set(): if self.system_ready.is_set(): message = {'event':'PiSensorUpdate'} @@ -97,8 +80,7 @@ def work(self): else: pass #self.pump_ready.clear() - - + print(readings) message['data'] = readings variables.r.publish(self.channel, json.dumps(message)) diff --git a/workers/pi/worker.py b/workers/pi/worker.py new file mode 100644 index 0000000..c3d009d --- /dev/null +++ b/workers/pi/worker.py @@ -0,0 +1,73 @@ +import time +import datetime +import json +import redis +import threading +import sys +sys.path.append('..') + +import variables + +def log(func): + def wrapper(*args, **kwargs): + print("MudPi Debug Log: " + " ".join([str(arg) for arg in args]) + " at " + str(datetime.datetime.now())) + value = func(*args, **kwargs) + return value + +# Base Worker Class +# A worker is responsible for handling its set of operations and running on a thread +class Worker(): + def __init__(self, config, main_thread_running, system_ready): + self.config = config + self.channel = config.get('channel', 'mudpi').replace(" ", "_").lower() + self.sleep_duration = config.get('sleep_duration', 15) + + # Threading Events to Keep Everything in Sync + self.main_thread_running = main_thread_running + self.system_ready = system_ready + self.worker_available = threading.Event() + + self.components = [] + return + + def init(self): + # print('Worker...\t\t\t\033[1;32m Initializing\033[0;0m'.format(**control)) + return + + def run(self): + t = threading.Thread(target=self.work, args=()) + t.start() + return t + + def work(self): + while self.main_thread_running.is_set(): + if self.system_ready.is_set(): + time.sleep(self.sleep_duration) + #This is only ran after the main thread is shut down + print("Worker Shutting Down...\t\033[1;32m Complete\033[0;0m") + + def dynamic_import(self, name): + # Split path of the class folder structure: {sensor name}_sensor . {SensorName}Sensor + components = name.split('.') + # Dynamically import root of component path + module = __import__(components[0]) + # Get component attributes + for component in components[1:]: + module = getattr(module, component) + return module + + def decodeMessageData(self, message): + if isinstance(message, dict): + #print('Dict Found') + return message + elif isinstance(message.decode('utf-8'), str): + try: + temp = json.loads(message.decode('utf-8')) + #print('Json Found') + return temp + except: + #print('Json Error. Str Found') + return {'event':'Unknown', 'data':message} + else: + #print('Failed to detect type') + return {'event':'Unknown', 'data':message} \ No newline at end of file diff --git a/workers/sensor_worker.py b/workers/sensor_worker.py deleted file mode 100644 index d069645..0000000 --- a/workers/sensor_worker.py +++ /dev/null @@ -1,127 +0,0 @@ -import time -import json -import threading -from nanpy import (SerialManager) -from nanpy.serialmanager import SerialManagerError -from nanpy.sockconnection import (SocketManager, SocketManagerError) -import sys -sys.path.append('..') - -import variables -import importlib - -#r = redis.Redis(host='127.0.0.1', port=6379) - -class SensorWorker(): - def __init__(self, config, main_thread_running, system_ready): - #self.config = {**config, **self.config} - self.config = config - self.main_thread_running = main_thread_running - self.system_ready = system_ready - self.node_ready = False - - attempts = 3 - if self.config.get('use_wifi', False): - while attempts > 0: - try: - attempts-= 1 - self.connection = SocketManager(host=str(self.config.get('address', 'mudpi.local'))) - self.sensors = [] - self.init_sensors() - except SocketManagerError: - print('[{name}] \033[1;33m Node Timeout\033[0;0m ['.format(**self.config), attempts, ' tries left]...') - time.sleep(15) - print('Retrying Connection...') - else: - print('[{name}] Wifi Connection \t\033[1;32m Success\033[0;0m'.format(**self.config)) - self.node_ready = True - break - else: - while attempts > 0: - try: - attempts-= 1 - self.connection = SerialManager(device=str(self.config.get('address', '/dev/ttyUSB1'))) - self.sensors = [] - self.init_sensors() - except SerialManagerError: - print('[{name}] \033[1;33m Node Timeout\033[0;0m ['.format(**self.config), attempts, ' tries left]...') - time.sleep(15) - print('Retrying Connection...') - else: - print('[{name}] Serial Connection \t\033[1;32m Success\033[0;0m'.format(**self.config)) - self.node_ready = True - break - - return - - def dynamic_sensor_import(self, path): - components = path.split('.') - - s = '' - for component in components[:-1]: - s += component + '.' - - parent = importlib.import_module(s[:-1]) - sensor = getattr(parent, components[-1]) - - return sensor - - def init_sensors(self): - for sensor in self.config['sensors']: - if sensor.get('type', None) is not None: - #Get the sensor from the sensors folder {sensor name}_sensor.{SensorName}Sensor - sensor_type = 'sensors.arduino.' + sensor.get('type').lower() + '_sensor.' + sensor.get('type').capitalize() + 'Sensor' - - #analog_pin_mode = False if sensor.get('is_digital', False) else True - - imported_sensor = self.dynamic_sensor_import(sensor_type) - #new_sensor = imported_sensor(sensor.get('pin'), name=sensor.get('name', sensor.get('type')), connection=self.connection, key=sensor.get('key', None)) - - # Define default kwargs for all sensor types, conditionally include optional variables below if they exist - sensor_kwargs = { - 'name' : sensor.get('name', sensor.get('type')), - 'pin' : int(sensor.get('pin')), - 'connection': self.connection, - 'key' : sensor.get('key', None) - } - - # optional sensor variables - # Model is specific to DHT modules to specify DHT11(11) DHT22(22) or DHT2301(21) - if sensor.get('model'): - sensor_kwargs['model'] = sensor.get('model') - - new_sensor = imported_sensor(**sensor_kwargs) - - new_sensor.init_sensor() - self.sensors.append(new_sensor) - print('{type} Sensor {pin}...\t\t\t\033[1;32m Ready\033[0;0m'.format(**sensor)) - return - - def run(self): - if self.node_ready: - t = threading.Thread(target=self.work, args=()) - t.start() - print(str(self.config['name']) +' Node Worker [' + str(len(self.config['sensors'])) + ' Sensors]...\t\033[1;32m Online\033[0;0m') - return t - else: - print("Node Connection...\t\t\t\033[1;31m Failed\033[0;0m") - return None - - def work(self): - - while self.main_thread_running.is_set(): - if self.system_ready.is_set() and self.node_ready: - message = {'event':'SensorUpdate'} - readings = {} - for sensor in self.sensors: - result = sensor.read() - readings[sensor.key] = result - #r.set(sensor.get('key', sensor.get('type')), value) - - print(readings) - message['data'] = readings - variables.r.publish('sensors', json.dumps(message)) - - time.sleep(15) - #This is only ran after the main thread is shut down - print("{name} Node Worker Shutting Down...\t\t\033[1;32m Complete\033[0;0m".format(**self.config)) \ No newline at end of file diff --git a/workers/trigger_worker.py b/workers/trigger_worker.py index 96bc941..1378fb9 100644 --- a/workers/trigger_worker.py +++ b/workers/trigger_worker.py @@ -3,6 +3,7 @@ import json import redis import threading +from .worker import Worker import sys sys.path.append('..') from triggers.trigger_group import TriggerGroup @@ -12,30 +13,15 @@ class TriggerWorker(): def __init__(self, config, main_thread_running, system_ready, actions): - #self.config = {**config, **self.config} - self.config = config - self.main_thread_running = main_thread_running - self.system_ready = system_ready + super().__init__(config, main_thread_running, system_ready) self.actions = actions self.triggers = [] self.trigger_threads = [] self.trigger_events = {} - self.init_triggers() + self.init() return - def dynamic_import(self, path): - components = path.split('.') - - s = '' - for component in components[:-1]: - s += component + '.' - - parent = importlib.import_module(s[:-1]) - sensor = getattr(parent, components[-1]) - - return sensor - - def init_triggers(self): + def init(self): trigger_index = 0 for trigger in self.config: if trigger.get("triggers", False): @@ -124,23 +110,20 @@ def init_trigger(self, config, trigger_index, group=None): return new_trigger def run(self): - t = threading.Thread(target=self.work, args=()) - t.start() print('Trigger Worker [' + str(len(self.config)) + ' Triggers]...\t\t\033[1;32m Online\033[0;0m') - return t + return super().run() def work(self): - while self.main_thread_running.is_set(): if self.system_ready.is_set(): - #Main Loop + # Main Loop time.sleep(1) time.sleep(2) - #This is only ran after the main thread is shut down + # This is only ran after the main thread is shut down for trigger in self.triggers: trigger.shutdown() - #Join all our sub threads for shutdown + # Join all our sub threads for shutdown for thread in self.trigger_threads: thread.join() print("Trigger Worker Shutting Down...\t\t\033[1;32m Complete\033[0;0m") \ No newline at end of file diff --git a/workers/worker.py b/workers/worker.py index c3d009d..c773c8b 100644 --- a/workers/worker.py +++ b/workers/worker.py @@ -45,6 +45,14 @@ def work(self): time.sleep(self.sleep_duration) #This is only ran after the main thread is shut down print("Worker Shutting Down...\t\033[1;32m Complete\033[0;0m") + + def elapsedTime(self): + self.time_elapsed = time.perf_counter() - self.time_start + return self.time_elapsed + + def resetElapsedTime(self): + self.time_start = time.perf_counter() + pass def dynamic_import(self, name): # Split path of the class folder structure: {sensor name}_sensor . {SensorName}Sensor From 660c6901c21ceb8d6ee123ad2fd5976e33ab2f6d Mon Sep 17 00:00:00 2001 From: Eric Davisson Date: Thu, 13 Aug 2020 20:38:05 -0500 Subject: [PATCH 58/97] Worker refactoring --- workers/trigger_worker.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/workers/trigger_worker.py b/workers/trigger_worker.py index 1378fb9..34eea1a 100644 --- a/workers/trigger_worker.py +++ b/workers/trigger_worker.py @@ -11,7 +11,7 @@ import variables import importlib -class TriggerWorker(): +class TriggerWorker(Worker): def __init__(self, config, main_thread_running, system_ready, actions): super().__init__(config, main_thread_running, system_ready) self.actions = actions From 0f7f2070253ff90158a8810bf5e15f597f17b505 Mon Sep 17 00:00:00 2001 From: Eric Davisson Date: Thu, 13 Aug 2020 20:39:43 -0500 Subject: [PATCH 59/97] More worker updates --- workers/worker.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/workers/worker.py b/workers/worker.py index c773c8b..6bc9b97 100644 --- a/workers/worker.py +++ b/workers/worker.py @@ -19,7 +19,6 @@ def wrapper(*args, **kwargs): class Worker(): def __init__(self, config, main_thread_running, system_ready): self.config = config - self.channel = config.get('channel', 'mudpi').replace(" ", "_").lower() self.sleep_duration = config.get('sleep_duration', 15) # Threading Events to Keep Everything in Sync @@ -45,7 +44,7 @@ def work(self): time.sleep(self.sleep_duration) #This is only ran after the main thread is shut down print("Worker Shutting Down...\t\033[1;32m Complete\033[0;0m") - + def elapsedTime(self): self.time_elapsed = time.perf_counter() - self.time_start return self.time_elapsed From 9e5aac0bb135f47e578d49d0a0fd7387588920ec Mon Sep 17 00:00:00 2001 From: Eric Davisson Date: Thu, 13 Aug 2020 20:48:29 -0500 Subject: [PATCH 60/97] Finishing Initial LCD Support --- mudpi.py | 5 +++++ workers/pi/lcd_worker.py | 22 ++++++++++++---------- workers/worker.py | 2 -- 3 files changed, 17 insertions(+), 12 deletions(-) diff --git a/mudpi.py b/mudpi.py index 878c723..6550d12 100755 --- a/mudpi.py +++ b/mudpi.py @@ -9,6 +9,7 @@ from action import Action from config_load import loadConfigJson from server.mudpi_server import MudpiServer +from workers.pi.lcd_worker import LCDWorker from workers.pi.i2c_worker import PiI2CWorker from workers.pi.relay_worker import RelayWorker from workers.pi.camera_worker import CameraWorker @@ -88,6 +89,7 @@ main_thread_running = threading.Event() # Event to signal workers to close system_ready = threading.Event() # Event to tell workers to begin working camera_available = threading.Event() # Event to signal if camera can be used + lcd_available = threading.Event() # Event to signal if lcd can be used main_thread_running.set() # Main event to tell workers to run/shutdown time.sleep(0.1) @@ -115,6 +117,9 @@ elif worker['type'] == "i2c": pw = PiI2CWorker(worker, main_thread_running, system_ready) print('MudPi I2C...\t\t\t\t\033[1;32m Initializing\033[0;0m') + elif worker['type'] == "lcd": + pw = LCDWorker(worker, main_thread_running, system_ready, lcd_available) + print('MudPi LCD...\t\t\t\t\033[1;32m Initializing\033[0;0m') elif worker['type'] == "relay": # Add Relay Worker Here for Better Config Control print('MudPi Relay...\t\t\t\033[1;32m Initializing\033[0;0m') diff --git a/workers/pi/lcd_worker.py b/workers/pi/lcd_worker.py index 70acf63..dd3cfc8 100644 --- a/workers/pi/lcd_worker.py +++ b/workers/pi/lcd_worker.py @@ -67,22 +67,25 @@ def handleMessage(self, message): except: print('Error Decoding Message for LCD') - def addMessageToQueue(self, message): + def addMessageToQueue(self, message, duration = 3): #Add message to queue if LCD available if self.lcd_available.is_set(): - if not self.active: - message = {'event':'StateChanged', 'data':1} - variables.r.set(self.config['key']+'_state', 1) - variables.r.publish(self.topic, json.dumps(message)) - self.active = True - #self.relay_active.set() This is handled by the redis listener now - self.resetElapsedTime() + + new_message = { + "message": message, + "duration": duration + } + self.message_queue.append(message) + + msg = { 'event':'MessageQueued', 'data': new_message } + variables.r.publish(self.topic, json.dumps(msg)) + + self.resetElapsedTime() def work(self): self.resetElapsedTime() while self.main_thread_running.is_set(): if self.system_ready.is_set(): - try: self.pubsub.get_message() if self.lcd_available.is_set(): @@ -91,7 +94,6 @@ def work(self): time.sleep(1) except: print("LCD Worker \t\033[1;31m Unexpected Error\033[0;0m".format(**self.config)) - else: #System not ready time.sleep(1) diff --git a/workers/worker.py b/workers/worker.py index 6bc9b97..90a3ae9 100644 --- a/workers/worker.py +++ b/workers/worker.py @@ -19,8 +19,6 @@ def wrapper(*args, **kwargs): class Worker(): def __init__(self, config, main_thread_running, system_ready): self.config = config - self.sleep_duration = config.get('sleep_duration', 15) - # Threading Events to Keep Everything in Sync self.main_thread_running = main_thread_running self.system_ready = system_ready From d1c85e75e4faf0ff349d1cd797233bf5a08f35bc Mon Sep 17 00:00:00 2001 From: Eric Davisson Date: Thu, 13 Aug 2020 20:53:48 -0500 Subject: [PATCH 61/97] Added worker load for LCD --- mudpi.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mudpi.py b/mudpi.py index 6550d12..5224060 100755 --- a/mudpi.py +++ b/mudpi.py @@ -118,8 +118,9 @@ pw = PiI2CWorker(worker, main_thread_running, system_ready) print('MudPi I2C...\t\t\t\t\033[1;32m Initializing\033[0;0m') elif worker['type'] == "lcd": - pw = LCDWorker(worker, main_thread_running, system_ready, lcd_available) - print('MudPi LCD...\t\t\t\t\033[1;32m Initializing\033[0;0m') + for lcd = in worker.lcds: + pw = LCDWorker(lcd, main_thread_running, system_ready, lcd_available) + print('MudPi LCD...\t\t\t\t\033[1;32m Initializing\033[0;0m') elif worker['type'] == "relay": # Add Relay Worker Here for Better Config Control print('MudPi Relay...\t\t\t\033[1;32m Initializing\033[0;0m') From a45d122a99e8f45dbfe7e78d46dc025b41a88bc9 Mon Sep 17 00:00:00 2001 From: Eric Davisson Date: Thu, 13 Aug 2020 20:54:18 -0500 Subject: [PATCH 62/97] bug fix --- mudpi.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mudpi.py b/mudpi.py index 5224060..43ba45c 100755 --- a/mudpi.py +++ b/mudpi.py @@ -118,7 +118,7 @@ pw = PiI2CWorker(worker, main_thread_running, system_ready) print('MudPi I2C...\t\t\t\t\033[1;32m Initializing\033[0;0m') elif worker['type'] == "lcd": - for lcd = in worker.lcds: + for lcd in worker.lcds: pw = LCDWorker(lcd, main_thread_running, system_ready, lcd_available) print('MudPi LCD...\t\t\t\t\033[1;32m Initializing\033[0;0m') elif worker['type'] == "relay": From 46f6258fd007c0efe2d1a955085d45b26a660a82 Mon Sep 17 00:00:00 2001 From: Eric Davisson Date: Thu, 13 Aug 2020 20:55:13 -0500 Subject: [PATCH 63/97] Bug fix on import --- mudpi.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mudpi.py b/mudpi.py index 43ba45c..f41d514 100755 --- a/mudpi.py +++ b/mudpi.py @@ -9,7 +9,7 @@ from action import Action from config_load import loadConfigJson from server.mudpi_server import MudpiServer -from workers.pi.lcd_worker import LCDWorker +from workers.pi.lcd_worker import LcdWorker from workers.pi.i2c_worker import PiI2CWorker from workers.pi.relay_worker import RelayWorker from workers.pi.camera_worker import CameraWorker @@ -119,7 +119,7 @@ print('MudPi I2C...\t\t\t\t\033[1;32m Initializing\033[0;0m') elif worker['type'] == "lcd": for lcd in worker.lcds: - pw = LCDWorker(lcd, main_thread_running, system_ready, lcd_available) + pw = LcdWorker(lcd, main_thread_running, system_ready, lcd_available) print('MudPi LCD...\t\t\t\t\033[1;32m Initializing\033[0;0m') elif worker['type'] == "relay": # Add Relay Worker Here for Better Config Control From 3386025e4fb133d5846d774676212d0612020fc2 Mon Sep 17 00:00:00 2001 From: Eric Davisson Date: Thu, 13 Aug 2020 20:56:03 -0500 Subject: [PATCH 64/97] Loop bug fix --- mudpi.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mudpi.py b/mudpi.py index f41d514..36a2dfa 100755 --- a/mudpi.py +++ b/mudpi.py @@ -118,7 +118,7 @@ pw = PiI2CWorker(worker, main_thread_running, system_ready) print('MudPi I2C...\t\t\t\t\033[1;32m Initializing\033[0;0m') elif worker['type'] == "lcd": - for lcd in worker.lcds: + for lcd in worker['lcds']: pw = LcdWorker(lcd, main_thread_running, system_ready, lcd_available) print('MudPi LCD...\t\t\t\t\033[1;32m Initializing\033[0;0m') elif worker['type'] == "relay": From ca9c1c6fad93c4111d6379bde725baa87cbbbeae Mon Sep 17 00:00:00 2001 From: Eric Davisson Date: Thu, 13 Aug 2020 20:59:01 -0500 Subject: [PATCH 65/97] Debugging LCD worker --- mudpi.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mudpi.py b/mudpi.py index 36a2dfa..2c46cca 100755 --- a/mudpi.py +++ b/mudpi.py @@ -127,8 +127,9 @@ else: raise Exception("Unknown Worker Type: " + worker['type']) workers.append(pw) - except KeyError: - print('MudPi Pi Workers...\t\t\033[1;31m Disabled\033[0;0m') + except KeyError as e: + print('MudPi Pi Workers...\t\t\t\033[1;31m Disabled\033[0;0m') + print(e) # Worker for relays attached to pi try: From af8fe1de212242be5d6ab95b11f5879eab4eb138 Mon Sep 17 00:00:00 2001 From: Eric Davisson Date: Thu, 13 Aug 2020 21:02:31 -0500 Subject: [PATCH 66/97] Exceptons for LCD worker --- mudpi.py | 1 - workers/pi/lcd_worker.py | 20 ++++++++++++++++---- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/mudpi.py b/mudpi.py index 2c46cca..9568f73 100755 --- a/mudpi.py +++ b/mudpi.py @@ -129,7 +129,6 @@ workers.append(pw) except KeyError as e: print('MudPi Pi Workers...\t\t\t\033[1;31m Disabled\033[0;0m') - print(e) # Worker for relays attached to pi try: diff --git a/workers/pi/lcd_worker.py b/workers/pi/lcd_worker.py index dd3cfc8..1a6f852 100644 --- a/workers/pi/lcd_worker.py +++ b/workers/pi/lcd_worker.py @@ -16,10 +16,22 @@ class LcdWorker(Worker): def __init__(self, config, main_thread_running, system_ready, lcd_available): super().__init__(config, main_thread_running, system_ready) - self.address = str(self.config['address']) if self.config['address'] is not None else None - self.model = str(self.config['model']) if self.config['model'] is not None else None - self.columns = int(self.config['columns']) if self.config['columns'] is not None else 16 - self.rows = int(self.config['rows']) if self.config['rows'] is not None else 2 + try: + self.address = str(self.config['address']) if self.config['address'] is not None else None + except KeyError: + self.address = None + try: + self.model = str(self.config['model']) if self.config['model'] is not None else None + except KeyError: + self.model = None + try: + self.columns = int(self.config['columns']) if self.config['columns'] is not None else 16 + except KeyError: + self.columns = 16 + try: + self.rows = int(self.config['rows']) if self.config['rows'] is not None else 2 + except KeyError: + self.rows = 2 #Events self.lcd_available = lcd_available From 5f5ffcbdb489204efb88d2baa8fbcf081f8bf6fe Mon Sep 17 00:00:00 2001 From: Eric Davisson Date: Thu, 13 Aug 2020 21:07:22 -0500 Subject: [PATCH 67/97] Error catches for LCD worker --- mudpi.py | 1 + workers/pi/lcd_worker.py | 9 ++++++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/mudpi.py b/mudpi.py index 9568f73..2c46cca 100755 --- a/mudpi.py +++ b/mudpi.py @@ -129,6 +129,7 @@ workers.append(pw) except KeyError as e: print('MudPi Pi Workers...\t\t\t\033[1;31m Disabled\033[0;0m') + print(e) # Worker for relays attached to pi try: diff --git a/workers/pi/lcd_worker.py b/workers/pi/lcd_worker.py index 1a6f852..d3a8d99 100644 --- a/workers/pi/lcd_worker.py +++ b/workers/pi/lcd_worker.py @@ -33,11 +33,14 @@ def __init__(self, config, main_thread_running, system_ready, lcd_available): except KeyError: self.rows = 2 - #Events + # Events self.lcd_available = lcd_available - #Dynamic Properties based on config - self.topic = self.config['topic'].replace(" ", "/").lower() if self.config['topic'] is not None else 'mudpi/lcd/' + # Dynamic Properties based on config + try: + self.topic = self.config['topic'].replace(" ", "/").lower() if self.config['topic'] is not None else 'mudpi/lcd/' + except KeyError: + self.topic = 'mudpi/lcd/' self.message_queue = [] #Pubsub Listeners From f84572441aea6bdf60cb56cd7eb914ed36c28803 Mon Sep 17 00:00:00 2001 From: Eric Davisson Date: Thu, 13 Aug 2020 21:08:25 -0500 Subject: [PATCH 68/97] Fixed default types --- workers/pi/lcd_worker.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/workers/pi/lcd_worker.py b/workers/pi/lcd_worker.py index d3a8d99..b6dcd1f 100644 --- a/workers/pi/lcd_worker.py +++ b/workers/pi/lcd_worker.py @@ -23,7 +23,7 @@ def __init__(self, config, main_thread_running, system_ready, lcd_available): try: self.model = str(self.config['model']) if self.config['model'] is not None else None except KeyError: - self.model = None + self.model = '' try: self.columns = int(self.config['columns']) if self.config['columns'] is not None else 16 except KeyError: From df59034326a9e82bb2fa4583938487fcb6e90a74 Mon Sep 17 00:00:00 2001 From: Eric Davisson Date: Thu, 13 Aug 2020 21:09:28 -0500 Subject: [PATCH 69/97] Fixed none type default --- workers/pi/lcd_worker.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/workers/pi/lcd_worker.py b/workers/pi/lcd_worker.py index b6dcd1f..5a82b7f 100644 --- a/workers/pi/lcd_worker.py +++ b/workers/pi/lcd_worker.py @@ -21,7 +21,7 @@ def __init__(self, config, main_thread_running, system_ready, lcd_available): except KeyError: self.address = None try: - self.model = str(self.config['model']) if self.config['model'] is not None else None + self.model = str(self.config['model']) if self.config['model'] is not None else '' except KeyError: self.model = '' try: From 2a0e3d22561c05c05e9f273271c75abbd163b2f3 Mon Sep 17 00:00:00 2001 From: Eric Davisson Date: Thu, 13 Aug 2020 21:11:25 -0500 Subject: [PATCH 70/97] Defaults for LCD model --- workers/pi/lcd_worker.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/workers/pi/lcd_worker.py b/workers/pi/lcd_worker.py index 5a82b7f..608cb35 100644 --- a/workers/pi/lcd_worker.py +++ b/workers/pi/lcd_worker.py @@ -52,8 +52,11 @@ def __init__(self, config, main_thread_running, system_ready, lcd_available): def init(self): # prepare sensor on specified pin - if (self.model.lower() == 'rgb'): - self.lcd = character_lcd.Character_LCD_RGB_I2C(self.i2c, self.columns, self.rows, self.address) + if(self.model): + if (self.model.lower() == 'rgb'): + self.lcd = character_lcd.Character_LCD_RGB_I2C(self.i2c, self.columns, self.rows, self.address) + else: + self.lcd = character_lcd.Character_LCD_I2C(self.i2c, self.columns, self.rows, self.address) else: self.lcd = character_lcd.Character_LCD_I2C(self.i2c, self.columns, self.rows, self.address) From 27faee13e598d65288f1b135f8237fbd33c4c48a Mon Sep 17 00:00:00 2001 From: Eric Davisson Date: Thu, 13 Aug 2020 21:14:27 -0500 Subject: [PATCH 71/97] Fixed init of LCD Worker --- workers/pi/lcd_worker.py | 1 + 1 file changed, 1 insertion(+) diff --git a/workers/pi/lcd_worker.py b/workers/pi/lcd_worker.py index 608cb35..99f591f 100644 --- a/workers/pi/lcd_worker.py +++ b/workers/pi/lcd_worker.py @@ -52,6 +52,7 @@ def __init__(self, config, main_thread_running, system_ready, lcd_available): def init(self): # prepare sensor on specified pin + self.i2c = I2C(board.SCL, board.SDA) if(self.model): if (self.model.lower() == 'rgb'): self.lcd = character_lcd.Character_LCD_RGB_I2C(self.i2c, self.columns, self.rows, self.address) From 79d988563f42e0cc9133ec9c3ec0136db67adaa4 Mon Sep 17 00:00:00 2001 From: Eric Davisson Date: Thu, 13 Aug 2020 21:15:43 -0500 Subject: [PATCH 72/97] Fixed init of LCD worker --- workers/pi/lcd_worker.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/workers/pi/lcd_worker.py b/workers/pi/lcd_worker.py index 99f591f..cc10eb3 100644 --- a/workers/pi/lcd_worker.py +++ b/workers/pi/lcd_worker.py @@ -52,7 +52,7 @@ def __init__(self, config, main_thread_running, system_ready, lcd_available): def init(self): # prepare sensor on specified pin - self.i2c = I2C(board.SCL, board.SDA) + self.i2c = busio.I2C(board.SCL, board.SDA) if(self.model): if (self.model.lower() == 'rgb'): self.lcd = character_lcd.Character_LCD_RGB_I2C(self.i2c, self.columns, self.rows, self.address) From cdabebf0ce343f61f917645a5dd75346d8ce5275 Mon Sep 17 00:00:00 2001 From: Eric Davisson Date: Thu, 13 Aug 2020 21:23:12 -0500 Subject: [PATCH 73/97] Fix address cast --- workers/pi/lcd_worker.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/workers/pi/lcd_worker.py b/workers/pi/lcd_worker.py index cc10eb3..346ac37 100644 --- a/workers/pi/lcd_worker.py +++ b/workers/pi/lcd_worker.py @@ -17,7 +17,7 @@ class LcdWorker(Worker): def __init__(self, config, main_thread_running, system_ready, lcd_available): super().__init__(config, main_thread_running, system_ready) try: - self.address = str(self.config['address']) if self.config['address'] is not None else None + self.address = int(self.config['address']) if self.config['address'] is not None else None except KeyError: self.address = None try: From 197e4286a4bb2dde1d0937659a9a900e39746d2c Mon Sep 17 00:00:00 2001 From: Eric Davisson Date: Thu, 13 Aug 2020 21:28:15 -0500 Subject: [PATCH 74/97] Fixes to base Worker Class --- workers/pi/lcd_worker.py | 4 ++-- workers/pi/worker.py | 8 ++++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/workers/pi/lcd_worker.py b/workers/pi/lcd_worker.py index 346ac37..cda8294 100644 --- a/workers/pi/lcd_worker.py +++ b/workers/pi/lcd_worker.py @@ -62,11 +62,11 @@ def init(self): self.lcd = character_lcd.Character_LCD_I2C(self.i2c, self.columns, self.rows, self.address) self.lcd.message = "MudPi\nGarden Online" - print('LCD Worker...\t\t\t\033[1;32m Initialized\033[0;0m'.format(**self.config)) + print('LCD Display Worker...\t\t\t\033[1;32m Initialized\033[0;0m'.format(**self.config)) return def run(self): - print('LCD Worker ...\t\t\t\033[1;32m Online\033[0;0m'.format(**self.config)) + print('LCD Display Worker ...\t\t\t\033[1;32m Online\033[0;0m'.format(**self.config)) return super().run() def handleMessage(self, message): diff --git a/workers/pi/worker.py b/workers/pi/worker.py index c3d009d..0d0409e 100644 --- a/workers/pi/worker.py +++ b/workers/pi/worker.py @@ -46,6 +46,14 @@ def work(self): #This is only ran after the main thread is shut down print("Worker Shutting Down...\t\033[1;32m Complete\033[0;0m") + def elapsedTime(self): + self.time_elapsed = time.perf_counter() - self.time_start + return self.time_elapsed + + def resetElapsedTime(self): + self.time_start = time.perf_counter() + pass + def dynamic_import(self, name): # Split path of the class folder structure: {sensor name}_sensor . {SensorName}Sensor components = name.split('.') From 03a9a8ed5801fe07af488d9ed8db5f99f5bcfdac Mon Sep 17 00:00:00 2001 From: Eric Davisson Date: Fri, 14 Aug 2020 07:10:50 -0500 Subject: [PATCH 75/97] Fixes to workers --- workers/arduino/arduino_worker.py | 6 +++--- workers/pi/lcd_worker.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/workers/arduino/arduino_worker.py b/workers/arduino/arduino_worker.py index 469f8d0..742283d 100644 --- a/workers/arduino/arduino_worker.py +++ b/workers/arduino/arduino_worker.py @@ -6,9 +6,9 @@ from nanpy import (SerialManager, ArduinoApi) from nanpy.serialmanager import SerialManagerError from nanpy.sockconnection import (SocketManager, SocketManagerError) -from workers.arduino_control_worker import ArduinoControlWorker -from workers.arduino_sensor_worker import ArduinoSensorWorker -from workers.arduino_relay_worker import ArduinoRelayWorker +from workers.arduino.arduino_control_worker import ArduinoControlWorker +from workers.arduino.arduino_sensor_worker import ArduinoSensorWorker +from workers.arduino.arduino_relay_worker import ArduinoRelayWorker import sys sys.path.append('..') diff --git a/workers/pi/lcd_worker.py b/workers/pi/lcd_worker.py index cda8294..595861d 100644 --- a/workers/pi/lcd_worker.py +++ b/workers/pi/lcd_worker.py @@ -123,4 +123,4 @@ def work(self): #This is only ran after the main thread is shut down #Close the pubsub connection self.pubsub.close() - print("LCD Worker Shutting Down...\t\033[1;32m Complete\033[0;0m".format(**self.config)) \ No newline at end of file + print("LCD Worker Shutting Down...\t\t\033[1;32m Complete\033[0;0m".format(**self.config)) \ No newline at end of file From c082986f01c6b45495a2861469141bf2ae42f928 Mon Sep 17 00:00:00 2001 From: Eric Davisson Date: Fri, 14 Aug 2020 11:46:18 -0500 Subject: [PATCH 76/97] Adding PCF model to LCD worker --- requirements.txt | 10 +++++----- workers/pi/lcd_worker.py | 5 ++++- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/requirements.txt b/requirements.txt index d2bb6fd..cb92516 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,9 +1,9 @@ -https://github.com/olixr/nanpy/archive/master.zip -Adafruit_DHT -adafruit-circuitpython-mcp3xxx -adafruit-blinka pycron redis picamera +Adafruit_DHT +adafruit-circuitpython-mcp3xxx +adafruit-blinka adafruit-circuitpython-bme680 -adafruit-circuitpython-charlcd \ No newline at end of file +https://github.com/Tim-Jackins/Adafruit_CircuitPython_CharLCD/archive/master.zip +https://github.com/olixr/nanpy/archive/master.zip \ No newline at end of file diff --git a/workers/pi/lcd_worker.py b/workers/pi/lcd_worker.py index 595861d..76ae72d 100644 --- a/workers/pi/lcd_worker.py +++ b/workers/pi/lcd_worker.py @@ -56,11 +56,13 @@ def init(self): if(self.model): if (self.model.lower() == 'rgb'): self.lcd = character_lcd.Character_LCD_RGB_I2C(self.i2c, self.columns, self.rows, self.address) + elif (self.model.lower() == 'pcf'): + self.lcd = character_lcd.Character_LCD_I2C(self.i2c, self.columns, self.rows, address=self.address usingPCF=True) else: self.lcd = character_lcd.Character_LCD_I2C(self.i2c, self.columns, self.rows, self.address) else: self.lcd = character_lcd.Character_LCD_I2C(self.i2c, self.columns, self.rows, self.address) - + self.lcd.clear() self.lcd.message = "MudPi\nGarden Online" print('LCD Display Worker...\t\t\t\033[1;32m Initialized\033[0;0m'.format(**self.config)) return @@ -103,6 +105,7 @@ def addMessageToQueue(self, message, duration = 3): def work(self): self.resetElapsedTime() + self.lcd.clear() while self.main_thread_running.is_set(): if self.system_ready.is_set(): try: From ac54091bb902c47402d8d4fe360a1beccb376f68 Mon Sep 17 00:00:00 2001 From: Eric Davisson Date: Fri, 14 Aug 2020 11:47:31 -0500 Subject: [PATCH 77/97] Bug fix syntax --- workers/pi/lcd_worker.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/workers/pi/lcd_worker.py b/workers/pi/lcd_worker.py index 76ae72d..2d3742c 100644 --- a/workers/pi/lcd_worker.py +++ b/workers/pi/lcd_worker.py @@ -57,7 +57,7 @@ def init(self): if (self.model.lower() == 'rgb'): self.lcd = character_lcd.Character_LCD_RGB_I2C(self.i2c, self.columns, self.rows, self.address) elif (self.model.lower() == 'pcf'): - self.lcd = character_lcd.Character_LCD_I2C(self.i2c, self.columns, self.rows, address=self.address usingPCF=True) + self.lcd = character_lcd.Character_LCD_I2C(self.i2c, self.columns, self.rows, address=self.address, usingPCF=True) else: self.lcd = character_lcd.Character_LCD_I2C(self.i2c, self.columns, self.rows, self.address) else: From b92a22f4b74b8b44c434e44f88db0e3be556aa5a Mon Sep 17 00:00:00 2001 From: Eric Davisson Date: Fri, 14 Aug 2020 13:06:24 -0500 Subject: [PATCH 78/97] Huge refactor to make more uniform - Remaining Worker Revamp - Changed channel to be topic through out the system and avoid future confusion --- requirements.txt | 4 +-- sensors/MCP3xxx/sensor.py | 6 ++--- sensors/MCP3xxx/soil_sensor.py | 2 +- tools/event_send_tool.py | 14 +++++----- triggers/control_trigger.py | 6 ++--- triggers/sensor_trigger.py | 6 ++--- workers/arduino/arduino_control_worker.py | 32 +++++++---------------- workers/arduino/arduino_relay_worker.py | 18 +++++-------- workers/arduino/arduino_sensor_worker.py | 4 +-- workers/arduino/arduino_worker.py | 24 ++++++++--------- workers/arduino/worker.py | 10 +++---- workers/pi/control_worker.py | 2 +- workers/pi/i2c_worker.py | 4 +-- workers/pi/sensor_worker.py | 4 +-- workers/pi/worker.py | 2 +- workers/trigger_worker.py | 4 +-- 16 files changed, 60 insertions(+), 82 deletions(-) diff --git a/requirements.txt b/requirements.txt index cb92516..77afc98 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,5 +5,5 @@ Adafruit_DHT adafruit-circuitpython-mcp3xxx adafruit-blinka adafruit-circuitpython-bme680 -https://github.com/Tim-Jackins/Adafruit_CircuitPython_CharLCD/archive/master.zip -https://github.com/olixr/nanpy/archive/master.zip \ No newline at end of file +https://github.com/olixr/nanpy/archive/master.zip +https://github.com/Tim-Jackins/Adafruit_CircuitPython_CharLCD/archive/master.zip \ No newline at end of file diff --git a/sensors/MCP3xxx/sensor.py b/sensors/MCP3xxx/sensor.py index e5eee87..973abaf 100644 --- a/sensors/MCP3xxx/sensor.py +++ b/sensors/MCP3xxx/sensor.py @@ -18,7 +18,7 @@ class Sensor: def __init__(self, pin: int, mcp, name='Sensor', key=None): self.pin = pin self.mcp = mcp - self.channel = None + self.topic = None self.name = name self.key = key.replace(" ", "_").lower() if key is not None else self.name\ @@ -43,8 +43,8 @@ def readRaw(self): Read the sensor(s) but return the raw voltage, useful for debugging :return: """ - return self.channel.voltage + return self.topic.voltage def readPin(self): """ Read the pin from the MCP3xxx as unaltered digital value""" - return self.channel.value + return self.topic.value diff --git a/sensors/MCP3xxx/soil_sensor.py b/sensors/MCP3xxx/soil_sensor.py index 01c760c..9a1822e 100644 --- a/sensors/MCP3xxx/soil_sensor.py +++ b/sensors/MCP3xxx/soil_sensor.py @@ -25,7 +25,7 @@ def __init__(self, pin, mcp, name='SoilSensor', key=None): return def init_sensor(self): - self.channel = AnalogIn(self.mcp, Sensor.PINS[self.pin]) + self.topic = AnalogIn(self.mcp, Sensor.PINS[self.pin]) def read(self): resistance = self.readPin() diff --git a/tools/event_send_tool.py b/tools/event_send_tool.py index 2d0ad5e..c9b7ee0 100644 --- a/tools/event_send_tool.py +++ b/tools/event_send_tool.py @@ -15,7 +15,7 @@ def timedMessage(message, delay=3): message = {} r = redis.Redis(host='127.0.0.1', port=6379, decode_responses=True) publisher = r - channel = None + topic = None while option != 0: #Clear the screen command print(chr(27) + "[2J") @@ -53,22 +53,22 @@ def timedMessage(message, delay=3): 'data': "/home/pi/Desktop/mudpi/img/mudpi-0039-2019-04-14-02-21.jpg", 'source': "camera_1" } - channel = 'garden/pi/camera' + topic = 'garden/pi/camera' else: timedMessage('Option not recognized') print(chr(27) + "[2J") continue - if channel is None: - channel = str(input('Enter Channel to Broadcast: ')) + if topic is None: + topic = str(input('Enter Topic to Broadcast: ')) - if channel is not None and channel != '': + if topic is not None and topic != '': #Publish the message - publisher.publish(channel, json.dumps(message)) + publisher.publish(topic, json.dumps(message)) print(message) timedMessage('Message Successfully Published!') else: - timedMessage('Channel Input Invalid') + timedMessage('Topic Input Invalid') time.sleep(2) print('Exit') diff --git a/triggers/control_trigger.py b/triggers/control_trigger.py index ee883a0..f4437fd 100644 --- a/triggers/control_trigger.py +++ b/triggers/control_trigger.py @@ -8,16 +8,16 @@ class ControlTrigger(Trigger): - def __init__(self, main_thread_running, system_ready, name='ControlTrigger',key=None, source=None, thresholds=None, channel="controls", trigger_active=None, frequency='once', actions=[], group=None): + def __init__(self, main_thread_running, system_ready, name='ControlTrigger',key=None, source=None, thresholds=None, topic="controls", trigger_active=None, frequency='once', actions=[], group=None): super().__init__(main_thread_running, system_ready, name=name, key=key, source=source, thresholds=thresholds, trigger_active=trigger_active, frequency=frequency, actions=actions, trigger_interval=0.5, group=group) - self.channel = channel.replace(" ", "_").lower() if channel is not None else "controls" + self.topic = topic.replace(" ", "_").lower() if topic is not None else "controls" return def init_trigger(self): #Initialize the trigger here (i.e. set listeners or create cron jobs) #Pubsub Listeners self.pubsub = variables.r.pubsub() - self.pubsub.subscribe(**{self.channel: self.handleEvent}) + self.pubsub.subscribe(**{self.topic: self.handleEvent}) pass def check(self): diff --git a/triggers/sensor_trigger.py b/triggers/sensor_trigger.py index 73cbcc2..93d85ff 100644 --- a/triggers/sensor_trigger.py +++ b/triggers/sensor_trigger.py @@ -8,9 +8,9 @@ class SensorTrigger(Trigger): - def __init__(self, main_thread_running, system_ready, name='SensorTrigger',key=None, source=None, nested_source=None, thresholds=None, channel="sensors", trigger_active=None, frequency='once', actions=[], group=None): + def __init__(self, main_thread_running, system_ready, name='SensorTrigger',key=None, source=None, nested_source=None, thresholds=None, topic="sensors", trigger_active=None, frequency='once', actions=[], group=None): super().__init__(main_thread_running, system_ready, name=name, key=key, source=source, thresholds=thresholds, trigger_active=trigger_active, frequency=frequency, actions=actions, trigger_interval=0.5, group=group) - self.channel = channel.replace(" ", "_").lower() if channel is not None else "sensors" + self.topic = topic.replace(" ", "_").lower() if topic is not None else "sensors" self.nested_source = nested_source.lower() if nested_source is not None else nested_source return @@ -18,7 +18,7 @@ def init_trigger(self): #Initialize the trigger here (i.e. set listeners or create cron jobs) #Pubsub Listeners self.pubsub = variables.r.pubsub() - self.pubsub.subscribe(**{self.channel: self.handleEvent}) + self.pubsub.subscribe(**{self.topic: self.handleEvent}) pass def check(self): diff --git a/workers/arduino/arduino_control_worker.py b/workers/arduino/arduino_control_worker.py index 4b39ebd..e6aec4a 100644 --- a/workers/arduino/arduino_control_worker.py +++ b/workers/arduino/arduino_control_worker.py @@ -6,6 +6,7 @@ from nanpy import (SerialManager) from nanpy.serialmanager import SerialManagerError from nanpy.sockconnection import (SocketManager, SocketManagerError) +from .worker import Worker import sys sys.path.append('..') @@ -14,35 +15,22 @@ #r = redis.Redis(host='127.0.0.1', port=6379) -class ArduinoControlWorker(): +class ArduinoControlWorker(Worker): def __init__(self, config, main_thread_running, system_ready, node_connected, connection=None): - #self.config = {**config, **self.config} - self.config = config - self.main_thread_running = main_thread_running - self.system_ready = system_ready + super().__init__(config, main_thread_running, system_ready) self.controls_ready = False self.node_connected = node_connected self.connection = connection self.controls = [] + if node_connected.is_set(): - self.init_controls() - self.controls_ready = True + self.init() + self.controls_ready = True return - def dynamic_import(self, path): - components = path.split('.') - - s = '' - for component in components[:-1]: - s += component + '.' - - parent = importlib.import_module(s[:-1]) - sensor = getattr(parent, components[-1]) - return sensor - - def init_controls(self): + def init(self): try: for control in self.config['controls']: if control.get('type', None) is not None: @@ -52,7 +40,6 @@ def init_controls(self): analog_pin_mode = False if control.get('is_digital', False) else True imported_control = self.dynamic_import(control_type) - #new_control = imported_control(control.get('pin'), name=control.get('name', control.get('type')), connection=self.connection, key=control.get('key', None)) # Define default kwargs for all control types, conditionally include optional variables below if they exist control_kwargs = { @@ -71,6 +58,7 @@ def init_controls(self): new_control.init_control() self.controls.append(new_control) + self.controls_ready = True print('{type} Control {pin}...\t\t\t\033[1;32m Ready\033[0;0m'.format(**control)) except (SerialManagerError, SocketManagerError, BrokenPipeError, ConnectionResetError, OSError, socket.timeout) as e: # Connection error. Reset everything for reconnect @@ -85,7 +73,6 @@ def run(self): return t def work(self): - while self.main_thread_running.is_set(): if self.system_ready.is_set(): if self.node_connected.is_set(): @@ -103,8 +90,7 @@ def work(self): time.sleep(15) else: # Worker connected but controls not initialized - self.init_controls() - self.controls_ready = True + self.init() else: # Node not connected. Wait for reconnect self.controls_ready = False diff --git a/workers/arduino/arduino_relay_worker.py b/workers/arduino/arduino_relay_worker.py index 2a0af72..77c5f45 100644 --- a/workers/arduino/arduino_relay_worker.py +++ b/workers/arduino/arduino_relay_worker.py @@ -8,33 +8,29 @@ from nanpy import (SerialManager, ArduinoApi) from nanpy.serialmanager import SerialManagerError from nanpy.sockconnection import (SocketManager, SocketManagerError) +from .worker import Worker sys.path.append('..') import variables -#r = redis.Redis(host='127.0.0.1', port=6379) - -# ToDO Update relay to make a key if one is not set in config - -class ArduinoRelayWorker(): +class ArduinoRelayWorker(Worker): def __init__(self, config, main_thread_running, system_ready, relay_available, relay_active, node_connected, connection=None, api=None): - #self.config = {**config, **self.config} - self.config = config - self.config['pin'] = int(self.config['pin']) #parse possbile strings to avoid errors + super().__init__(config, main_thread_running, system_ready) + self.config['pin'] = int(self.config['pin']) # parse possbile strings to avoid errors - #Events + # Events self.main_thread_running = main_thread_running self.system_ready = system_ready self.relay_available = relay_available self.relay_active = relay_active self.node_connected = node_connected - #Dynamic Properties based on config + # Dynamic Properties based on config self.active = False self.relay_ready = False self.topic = self.config['topic'].replace(" ", "/").lower() if self.config['topic'] is not None else 'mudpi/relay/*' - #Pubsub Listeners + # Pubsub Listeners self.pubsub = variables.r.pubsub() self.pubsub.subscribe(**{self.topic: self.handleMessage}) self.api = api diff --git a/workers/arduino/arduino_sensor_worker.py b/workers/arduino/arduino_sensor_worker.py index 4819042..3ffc15c 100644 --- a/workers/arduino/arduino_sensor_worker.py +++ b/workers/arduino/arduino_sensor_worker.py @@ -21,7 +21,7 @@ def __init__(self, config, main_thread_running, system_ready, node_connected, co self.main_thread_running = main_thread_running self.system_ready = system_ready self.sleep_duration = config.get('sleep_duration', 15) - self.channel = config.get('channel', 'sensors').replace(" ", "_").lower() + self.topic = config.get('topic', 'sensors').replace(" ", "_").lower() self.sensors_ready = False self.node_connected = node_connected self.connection = connection @@ -105,7 +105,7 @@ def work(self): print("Node Readings: ", readings) message['data'] = readings - variables.r.publish(self.channel, json.dumps(message)) + variables.r.publish(self.topic, json.dumps(message)) except (SerialManagerError, SocketManagerError, BrokenPipeError, ConnectionResetError, OSError, socket.timeout) as e: print('\033[1;36m{name}\033[0;0m -> \033[1;33mSensors Timeout!\033[0;0m'.format(**self.config)) self.sensors = [] diff --git a/workers/arduino/arduino_worker.py b/workers/arduino/arduino_worker.py index 742283d..95c0464 100644 --- a/workers/arduino/arduino_worker.py +++ b/workers/arduino/arduino_worker.py @@ -9,6 +9,7 @@ from workers.arduino.arduino_control_worker import ArduinoControlWorker from workers.arduino.arduino_sensor_worker import ArduinoSensorWorker from workers.arduino.arduino_relay_worker import ArduinoRelayWorker +from .worker import Worker import sys sys.path.append('..') @@ -17,22 +18,20 @@ #r = redis.Redis(host='127.0.0.1', port=6379) -class ArduinoWorker(): +class ArduinoWorker(Worker): def __init__(self, config, main_thread_running, system_ready, connection=None): - #self.config = {**config, **self.config} - self.config = config - self.main_thread_running = main_thread_running - self.system_ready = system_ready - self.sleep_duration = config.get('sleep_duration', 15) + super().__init__(config, main_thread_running, system_ready) self.connection = connection self.threads = [] + + # Events self.node_ready = threading.Event() - self.node_connected = threading.Event() #Event to signal if camera can be used + self.node_connected = threading.Event() # Event to signal if node can be used + self.workers = [] self.relays = [] self.relayEvents = {} self.relay_index = 0 - self.api = None if connection is None: self.connection = self.connect() @@ -141,8 +140,9 @@ def resetConnection(self): def run(self): for worker in self.workers: - self.threads.append(worker.run()) - time.sleep(4) + t = worker.run() + self.threads.append(t) + time.sleep(1) t = threading.Thread(target=self.work, args=()) t.start() @@ -182,8 +182,8 @@ def work(self): # Main loop delay between cycles time.sleep(self.sleep_duration) - #This is only ran after the main thread is shut down - #Join all our sub threads for shutdown + # This is only ran after the main thread is shut down + # Join all our sub threads for shutdown for thread in self.threads: thread.join() print("{name} Shutting Down...\t\t\033[1;32m Complete\033[0;0m".format(**self.config)) \ No newline at end of file diff --git a/workers/arduino/worker.py b/workers/arduino/worker.py index c3d009d..3f94ed0 100644 --- a/workers/arduino/worker.py +++ b/workers/arduino/worker.py @@ -3,23 +3,18 @@ import json import redis import threading +from .worker import Worker import sys sys.path.append('..') import variables -def log(func): - def wrapper(*args, **kwargs): - print("MudPi Debug Log: " + " ".join([str(arg) for arg in args]) + " at " + str(datetime.datetime.now())) - value = func(*args, **kwargs) - return value - # Base Worker Class # A worker is responsible for handling its set of operations and running on a thread class Worker(): def __init__(self, config, main_thread_running, system_ready): self.config = config - self.channel = config.get('channel', 'mudpi').replace(" ", "_").lower() + self.topic = config.get('topic', 'mudpi').replace(" ", "_").lower() self.sleep_duration = config.get('sleep_duration', 15) # Threading Events to Keep Everything in Sync @@ -27,6 +22,7 @@ def __init__(self, config, main_thread_running, system_ready): self.system_ready = system_ready self.worker_available = threading.Event() + self.api = None self.components = [] return diff --git a/workers/pi/control_worker.py b/workers/pi/control_worker.py index d254d9e..3a399fc 100644 --- a/workers/pi/control_worker.py +++ b/workers/pi/control_worker.py @@ -14,7 +14,7 @@ class PiControlWorker(Worker): def __init__(self, config, main_thread_running, system_ready): super().__init__(config, main_thread_running, system_ready) - self.channel = config.get('channel', 'controls').replace(" ", "_").lower() + self.topic = config.get('topic', 'controls').replace(" ", "_").lower() self.sleep_duration = config.get('sleep_duration', 0.5) self.controls = [] diff --git a/workers/pi/i2c_worker.py b/workers/pi/i2c_worker.py index 96341fd..4f473d5 100644 --- a/workers/pi/i2c_worker.py +++ b/workers/pi/i2c_worker.py @@ -16,7 +16,7 @@ class PiI2CWorker(Worker): def __init__(self, config, main_thread_running, system_ready): super().__init__(config, main_thread_running, system_ready) - self.channel = config.get('channel', 'i2c').replace(" ", "_").lower() + self.topic = config.get('topic', 'i2c').replace(" ", "_").lower() self.sleep_duration = config.get('sleep_duration', 30) self.sensors = [] @@ -71,7 +71,7 @@ def work(self): message['data'] = readings print(readings); - variables.r.publish(self.channel, json.dumps(message)) + variables.r.publish(self.topic, json.dumps(message)) time.sleep(self.sleep_duration) time.sleep(2) diff --git a/workers/pi/sensor_worker.py b/workers/pi/sensor_worker.py index ea37ac7..b5231d5 100644 --- a/workers/pi/sensor_worker.py +++ b/workers/pi/sensor_worker.py @@ -15,7 +15,7 @@ class PiSensorWorker(Worker): def __init__(self, config, main_thread_running, system_ready): super().__init__(config, main_thread_running, system_ready) - self.channel = config.get('channel', 'sensors').replace(" ", "_").lower() + self.topic = config.get('topic', 'sensors').replace(" ", "_").lower() self.sleep_duration = config.get('sleep_duration', 30) self.sensors = [] @@ -83,7 +83,7 @@ def work(self): print(readings) message['data'] = readings - variables.r.publish(self.channel, json.dumps(message)) + variables.r.publish(self.topic, json.dumps(message)) time.sleep(self.sleep_duration) time.sleep(2) diff --git a/workers/pi/worker.py b/workers/pi/worker.py index 0d0409e..90e9bf0 100644 --- a/workers/pi/worker.py +++ b/workers/pi/worker.py @@ -19,7 +19,7 @@ def wrapper(*args, **kwargs): class Worker(): def __init__(self, config, main_thread_running, system_ready): self.config = config - self.channel = config.get('channel', 'mudpi').replace(" ", "_").lower() + self.topic = config.get('topic', 'mudpi').replace(" ", "_").lower() self.sleep_duration = config.get('sleep_duration', 15) # Threading Events to Keep Everything in Sync diff --git a/workers/trigger_worker.py b/workers/trigger_worker.py index 34eea1a..de355d2 100644 --- a/workers/trigger_worker.py +++ b/workers/trigger_worker.py @@ -93,8 +93,8 @@ def init_trigger(self, config, trigger_index, group=None): if config.get('nested_source'): trigger_kwargs['nested_source'] = config.get('nested_source') - if config.get('channel'): - trigger_kwargs['channel'] = config.get('channel') + if config.get('topic'): + trigger_kwargs['topic'] = config.get('topic') if config.get('thresholds'): trigger_kwargs['thresholds'] = config.get('thresholds') From 507c233ae490fca8252510a73beb762bbbb586f0 Mon Sep 17 00:00:00 2001 From: Eric Davisson Date: Fri, 14 Aug 2020 13:07:43 -0500 Subject: [PATCH 79/97] Fixed redundant import --- workers/arduino/worker.py | 1 - 1 file changed, 1 deletion(-) diff --git a/workers/arduino/worker.py b/workers/arduino/worker.py index 3f94ed0..71f0551 100644 --- a/workers/arduino/worker.py +++ b/workers/arduino/worker.py @@ -3,7 +3,6 @@ import json import redis import threading -from .worker import Worker import sys sys.path.append('..') From 462e35f274ccf3139299e76c131bd263af26249b Mon Sep 17 00:00:00 2001 From: Eric Davisson Date: Fri, 14 Aug 2020 14:11:30 -0500 Subject: [PATCH 80/97] LCD message queue and tester --- mudpi.py | 7 ++- tools/lcd_message_tool.py | 96 +++++++++++++++++++++++++++++++ workers/arduino/arduino_worker.py | 3 - workers/pi/lcd_worker.py | 44 +++++++++++--- 4 files changed, 137 insertions(+), 13 deletions(-) create mode 100644 tools/lcd_message_tool.py diff --git a/mudpi.py b/mudpi.py index 2c46cca..44920dd 100755 --- a/mudpi.py +++ b/mudpi.py @@ -120,7 +120,7 @@ elif worker['type'] == "lcd": for lcd in worker['lcds']: pw = LcdWorker(lcd, main_thread_running, system_ready, lcd_available) - print('MudPi LCD...\t\t\t\t\033[1;32m Initializing\033[0;0m') + print('MudPi LCD Displays...\t\t\t\033[1;32m Initializing\033[0;0m') elif worker['type'] == "relay": # Add Relay Worker Here for Better Config Control print('MudPi Relay...\t\t\t\033[1;32m Initializing\033[0;0m') @@ -196,10 +196,11 @@ threads.append(s) s.start() except KeyError: - print('No Server Config Found to Load') + print('MudPi Socket Server...\t\t\033[1;31m Disabled\033[0;0m') - print('MudPi Garden Control...\t\t\t\033[1;32m Initialized\033[0;0m') + print('MudPi Garden Controls...\t\t\t\033[1;32m Initialized\033[0;0m') + print('Signaling MudPi Controls...\t\t\t\033[1;32m Online\033[0;0m') for worker in workers: t = worker.run() threads.append(t) diff --git a/tools/lcd_message_tool.py b/tools/lcd_message_tool.py new file mode 100644 index 0000000..aefd82e --- /dev/null +++ b/tools/lcd_message_tool.py @@ -0,0 +1,96 @@ +import redis +import threading +import json +import time + +def timedMessage(message, delay=3): + for s in range(1,delay): + remainingTime = delay - s + print(message + '...{0}s \r'.format(remainingTime), end="", flush=True) + time.sleep(s) + +if __name__ == "__main__": + try: + option = True + message = {} + r = redis.Redis(host='127.0.0.1', port=6379, decode_responses=True) + publisher = r + topic = None + while option != 0: + #Clear the screen command + print(chr(27) + "[2J") + print('--------- LCD MudPi ---------') + print('|4. Clear Message Queue |') + print('|3. Clear Display |') + print('|2. Test Message |') + print('|1. Add Message |') + print('|0. Shutdown |') + print('-------------------------------') + try: + option = int(input('Enter Option: ')) + except: + #Catch string input error + option = 9 + if option != 0: + if option == 1: + try: + msg = { + "message":"", + "duration":10 + } + msg["message"] = int(input('Enter Message to Display: ')) + msg["duration"] = int(input('Enter Duration to Display (seconds): ')) + + except: + msg = { + "message":"Error Test", + "duration":10 + } + message = { + 'event': 'Message', + 'data': msg + } + elif option == 2: + msg = { + "message":"Test Message\nMudPi Test", + "duration":15 + } + message = { + 'event': 'Message', + 'data': msg + } + elif option == 3: + message = { + 'event': 'Clear', + 'data': 1 + } + elif option == 4: + message = { + 'event': 'ClearQueue', + 'data': 1 + } + else: + timedMessage('Option not recognized') + print(chr(27) + "[2J") + continue + + if topic is None: + topic = str(input('Enter the LCD Topic to Broadcast: ')) + + if topic is not None and topic != '': + #Publish the message + publisher.publish(topic, json.dumps(message)) + print(message) + timedMessage('Message Successfully Queued!') + else: + timedMessage('Topic Input Invalid') + time.sleep(2) + + print('Exit') + except KeyboardInterrupt: + #Kill The Server + #r.publish('test', json.dumps({'EXIT':True})) + print('LCD Message Program Terminated...') + finally: + pass + diff --git a/workers/arduino/arduino_worker.py b/workers/arduino/arduino_worker.py index 95c0464..3a85770 100644 --- a/workers/arduino/arduino_worker.py +++ b/workers/arduino/arduino_worker.py @@ -39,8 +39,6 @@ def __init__(self, config, main_thread_running, system_ready, connection=None): if self.config['controls'] is not None: acw = ArduinoControlWorker(self.config, main_thread_running, system_ready, self.node_connected, self.connection) self.workers.append(acw) - if acw is not None: - self.threads.append(acw) time.sleep(3) except KeyError: print('{name} Node Controls...\t\t\033[1;31m Disabled\033[0;0m'.format(**self.config)) @@ -88,7 +86,6 @@ def connect(self): self.api = ArduinoApi(connection=conn) except (SocketManagerError, BrokenPipeError, ConnectionResetError, socket.timeout) as e: print('{name} -> Connecting...\t\t\033[1;33m Timeout\033[0;0m '.format(**self.config)) - # print(e); if attempts > 0: print('{name} -> Preparing Reconnect... \t'.format(**self.config)) else: diff --git a/workers/pi/lcd_worker.py b/workers/pi/lcd_worker.py index 2d3742c..2f66c11 100644 --- a/workers/pi/lcd_worker.py +++ b/workers/pi/lcd_worker.py @@ -32,18 +32,28 @@ def __init__(self, config, main_thread_running, system_ready, lcd_available): self.rows = int(self.config['rows']) if self.config['rows'] is not None else 2 except KeyError: self.rows = 2 + try: + self.address = int(self.config['address']) if self.config['address'] is not None else None + except KeyError: + try: + self.default_duration = int(self.config['default_duration']) if self.config['default_duration'] is not None else 5 + except KeyError: + self.default_duration = 5 + self.current_message = "" + self.cached_message = {} + self.need_new_message = True # Events self.lcd_available = lcd_available # Dynamic Properties based on config try: - self.topic = self.config['topic'].replace(" ", "/").lower() if self.config['topic'] is not None else 'mudpi/lcd/' + self.topic = self.config['topic'].replace(" ", "/").lower() if self.config['topic'] is not None else 'mudpi/lcd/*' except KeyError: - self.topic = 'mudpi/lcd/' + self.topic = 'mudpi/lcd/*' self.message_queue = [] - #Pubsub Listeners + # Pubsub Listeners self.pubsub = variables.r.pubsub() self.pubsub.subscribe(**{self.topic: self.handleMessage}) @@ -51,6 +61,7 @@ def __init__(self, config, main_thread_running, system_ready, lcd_available): return def init(self): + print('LCD Display Worker...\t\t\t\033[1;32m Initializing\033[0;0m'.format(**self.config)) # prepare sensor on specified pin self.i2c = busio.I2C(board.SCL, board.SDA) if(self.model): @@ -64,7 +75,6 @@ def init(self): self.lcd = character_lcd.Character_LCD_I2C(self.i2c, self.columns, self.rows, self.address) self.lcd.clear() self.lcd.message = "MudPi\nGarden Online" - print('LCD Display Worker...\t\t\t\033[1;32m Initialized\033[0;0m'.format(**self.config)) return def run(self): @@ -78,13 +88,16 @@ def handleMessage(self, message): try: if decoded_message['event'] == 'Message': if decoded_message.get('data', None): - self.lcd.message = decoded_message.get('data', '') + self.addMessageToQueue(decoded_message.get('data', ''), decoded_message.get('duration', self.default_duration)) elif decoded_message.get('data', None) == 0: self.lcd.clear() print('LCD Message to \033[1;36m{0}\033[0;0m'.format(decoded_message['data'])) elif decoded_message['event'] == 'Clear': self.lcd.clear() print('Cleared the LCD Screen') + elif decoded_message['event'] == 'ClearQueue': + self.message_queue = [] + print('Cleared the LCD Message Queue') except: print('Error Decoding Message for LCD') @@ -100,18 +113,35 @@ def addMessageToQueue(self, message, duration = 3): msg = { 'event':'MessageQueued', 'data': new_message } variables.r.publish(self.topic, json.dumps(msg)) + return - self.resetElapsedTime() + def nextMessageFromQueue(): + if len(self.message_queue) > 0: + self.need_new_message = False + self.resetElapsedTime() + return self.message_queue.pop(0) + else: + return None def work(self): self.resetElapsedTime() self.lcd.clear() + self.need_new_message = True while self.main_thread_running.is_set(): if self.system_ready.is_set(): try: self.pubsub.get_message() if self.lcd_available.is_set(): - pass + if self.cached_message and not need_new_message: + if self.current_message != self.cached_message.message: + self.lcd.clear() + self.lcd.message = self.cached_message.message + self.current_message = self.cached_message.message # store message to only display once and prevent flickers + if self.elapsedTime() > self.cached_message.duration + 1: + self.need_new_message = True + else: + # Get first time message after clear + self.cached_message = self.nextMessageFromQueue() else: time.sleep(1) except: From a017c08e344c2a157d3e3084387a332fca9eba00 Mon Sep 17 00:00:00 2001 From: Eric Davisson Date: Fri, 14 Aug 2020 14:13:43 -0500 Subject: [PATCH 81/97] Fixed indentation --- workers/pi/lcd_worker.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/workers/pi/lcd_worker.py b/workers/pi/lcd_worker.py index 2f66c11..0d8da3d 100644 --- a/workers/pi/lcd_worker.py +++ b/workers/pi/lcd_worker.py @@ -35,7 +35,7 @@ def __init__(self, config, main_thread_running, system_ready, lcd_available): try: self.address = int(self.config['address']) if self.config['address'] is not None else None except KeyError: - try: + try: self.default_duration = int(self.config['default_duration']) if self.config['default_duration'] is not None else 5 except KeyError: self.default_duration = 5 From 9ebd2904aebd0dad125571a6e4a982c46d28f112 Mon Sep 17 00:00:00 2001 From: Eric Davisson Date: Fri, 14 Aug 2020 14:15:33 -0500 Subject: [PATCH 82/97] Fixed except clause --- workers/pi/lcd_worker.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/workers/pi/lcd_worker.py b/workers/pi/lcd_worker.py index 0d8da3d..32adf1d 100644 --- a/workers/pi/lcd_worker.py +++ b/workers/pi/lcd_worker.py @@ -35,10 +35,12 @@ def __init__(self, config, main_thread_running, system_ready, lcd_available): try: self.address = int(self.config['address']) if self.config['address'] is not None else None except KeyError: + self.address = None try: self.default_duration = int(self.config['default_duration']) if self.config['default_duration'] is not None else 5 except KeyError: self.default_duration = 5 + self.current_message = "" self.cached_message = {} self.need_new_message = True From d4a0cff129ed055f78b4ed14db2765cebfe73ed8 Mon Sep 17 00:00:00 2001 From: Eric Davisson Date: Fri, 14 Aug 2020 14:27:36 -0500 Subject: [PATCH 83/97] Updates to LCD worker --- mudpi.py | 6 +++--- sensors/pi/humidity_sensor.py | 2 +- workers/pi/lcd_worker.py | 12 +++++------- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/mudpi.py b/mudpi.py index 44920dd..2b567e7 100755 --- a/mudpi.py +++ b/mudpi.py @@ -196,11 +196,11 @@ threads.append(s) s.start() except KeyError: - print('MudPi Socket Server...\t\t\033[1;31m Disabled\033[0;0m') + print('MudPi Socket Server...\t\t\t\033[1;31m Disabled\033[0;0m') - print('MudPi Garden Controls...\t\t\t\033[1;32m Initialized\033[0;0m') - print('Signaling MudPi Controls...\t\t\t\033[1;32m Online\033[0;0m') + print('MudPi Garden Controls...\t\t\033[1;32m Initialized\033[0;0m') + print('Signaling MudPi Controls...\t\t\033[1;32m Online\033[0;0m') for worker in workers: t = worker.run() threads.append(t) diff --git a/sensors/pi/humidity_sensor.py b/sensors/pi/humidity_sensor.py index 238705e..1317b72 100644 --- a/sensors/pi/humidity_sensor.py +++ b/sensors/pi/humidity_sensor.py @@ -43,7 +43,7 @@ def read(self): variables.r.set(self.key, json.dumps(readings)) return readings else: - print('Failed to get reading. Try again!') + print('Failed to get DHT reading. Try again!') def readRaw(self): diff --git a/workers/pi/lcd_worker.py b/workers/pi/lcd_worker.py index 32adf1d..2cb14ab 100644 --- a/workers/pi/lcd_worker.py +++ b/workers/pi/lcd_worker.py @@ -44,16 +44,16 @@ def __init__(self, config, main_thread_running, system_ready, lcd_available): self.current_message = "" self.cached_message = {} self.need_new_message = True + self.message_queue = [] # Events self.lcd_available = lcd_available # Dynamic Properties based on config try: - self.topic = self.config['topic'].replace(" ", "/").lower() if self.config['topic'] is not None else 'mudpi/lcd/*' + self.topic = self.config['topic'].replace(" ", "/").lower() if self.config['topic'] is not None else 'mudpi/lcd' except KeyError: - self.topic = 'mudpi/lcd/*' - self.message_queue = [] + self.topic = 'mudpi/lcd' # Pubsub Listeners self.pubsub = variables.r.pubsub() @@ -90,10 +90,8 @@ def handleMessage(self, message): try: if decoded_message['event'] == 'Message': if decoded_message.get('data', None): - self.addMessageToQueue(decoded_message.get('data', ''), decoded_message.get('duration', self.default_duration)) - elif decoded_message.get('data', None) == 0: - self.lcd.clear() - print('LCD Message to \033[1;36m{0}\033[0;0m'.format(decoded_message['data'])) + self.addMessageToQueue(decoded_message['data'].get('data', ''), decoded_message['data'].get('duration', self.default_duration)) + print('LCD New Message: \033[1;36m{0}\033[0;0m'.format(decoded_message['data'])) elif decoded_message['event'] == 'Clear': self.lcd.clear() print('Cleared the LCD Screen') From 600188232e0f0c3053d6eb2a4a2a6bb54eaeab45 Mon Sep 17 00:00:00 2001 From: Eric Davisson Date: Fri, 14 Aug 2020 14:43:10 -0500 Subject: [PATCH 84/97] More refactoring --- mudpi.py | 3 ++- workers/arduino/arduino_sensor_worker.py | 26 ++++++------------------ workers/arduino/arduino_worker.py | 5 +++-- 3 files changed, 11 insertions(+), 23 deletions(-) diff --git a/mudpi.py b/mudpi.py index 2b567e7..6577470 100755 --- a/mudpi.py +++ b/mudpi.py @@ -89,7 +89,7 @@ main_thread_running = threading.Event() # Event to signal workers to close system_ready = threading.Event() # Event to tell workers to begin working camera_available = threading.Event() # Event to signal if camera can be used - lcd_available = threading.Event() # Event to signal if lcd can be used + lcd_available = threading.Event() # Event to signal if lcd displays can be used main_thread_running.set() # Main event to tell workers to run/shutdown time.sleep(0.1) @@ -120,6 +120,7 @@ elif worker['type'] == "lcd": for lcd in worker['lcds']: pw = LcdWorker(lcd, main_thread_running, system_ready, lcd_available) + lcd_available.set() print('MudPi LCD Displays...\t\t\t\033[1;32m Initializing\033[0;0m') elif worker['type'] == "relay": # Add Relay Worker Here for Better Config Control diff --git a/workers/arduino/arduino_sensor_worker.py b/workers/arduino/arduino_sensor_worker.py index 3ffc15c..d685041 100644 --- a/workers/arduino/arduino_sensor_worker.py +++ b/workers/arduino/arduino_sensor_worker.py @@ -6,6 +6,7 @@ from nanpy import (SerialManager) from nanpy.serialmanager import SerialManagerError from nanpy.sockconnection import (SocketManager, SocketManagerError) +from .worker import Worker import sys sys.path.append('..') @@ -14,13 +15,9 @@ #r = redis.Redis(host='127.0.0.1', port=6379) -class ArduinoSensorWorker(): +class ArduinoSensorWorker(Worker): def __init__(self, config, main_thread_running, system_ready, node_connected, connection=None, api=None): - #self.config = {**config, **self.config} - self.config = config - self.main_thread_running = main_thread_running - self.system_ready = system_ready - self.sleep_duration = config.get('sleep_duration', 15) + super().__init__(config, main_thread_running, system_ready) self.topic = config.get('topic', 'sensors').replace(" ", "_").lower() self.sensors_ready = False self.node_connected = node_connected @@ -28,23 +25,11 @@ def __init__(self, config, main_thread_running, system_ready, node_connected, co self.api = api self.sensors = [] if node_connected.is_set(): - self.init_sensors() + self.init() self.sensors_ready = True return - def dynamic_import(self, path): - components = path.split('.') - - s = '' - for component in components[:-1]: - s += component + '.' - - parent = importlib.import_module(s[:-1]) - sensor = getattr(parent, components[-1]) - - return sensor - - def init_sensors(self, connection=None): + def init(self, connection=None): print('{name} Sensor Worker...\t\t\033[1;32m Preparing\033[0;0m'.format(**self.config)) try: for sensor in self.config['sensors']: @@ -88,6 +73,7 @@ def init_sensors(self, connection=None): def run(self): t = threading.Thread(target=self.work, args=()) t.start() + print('Node {name} Sensor Worker...\t\t\033[1;32m Online\033[0;0m'.format(**self.config)) return t def work(self): diff --git a/workers/arduino/arduino_worker.py b/workers/arduino/arduino_worker.py index 3a85770..f39a1d0 100644 --- a/workers/arduino/arduino_worker.py +++ b/workers/arduino/arduino_worker.py @@ -32,8 +32,6 @@ def __init__(self, config, main_thread_running, system_ready, connection=None): self.relays = [] self.relayEvents = {} self.relay_index = 0 - if connection is None: - self.connection = self.connect() try: if self.config['controls'] is not None: @@ -136,6 +134,9 @@ def resetConnection(self): def run(self): + if connection is None: + self.connection = self.connect() + for worker in self.workers: t = worker.run() self.threads.append(t) From 5a7718dd50d82c7cdef7fdeac600df3eadf41cd8 Mon Sep 17 00:00:00 2001 From: Eric Davisson Date: Fri, 14 Aug 2020 14:44:40 -0500 Subject: [PATCH 85/97] Arduino worker fix --- workers/arduino/arduino_worker.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/workers/arduino/arduino_worker.py b/workers/arduino/arduino_worker.py index f39a1d0..9811d4a 100644 --- a/workers/arduino/arduino_worker.py +++ b/workers/arduino/arduino_worker.py @@ -134,9 +134,9 @@ def resetConnection(self): def run(self): - if connection is None: + if self.connection is None: self.connection = self.connect() - + for worker in self.workers: t = worker.run() self.threads.append(t) From 893f843e016b70cde2ba2a61f6b003b5ba9677bb Mon Sep 17 00:00:00 2001 From: Eric Davisson Date: Fri, 14 Aug 2020 14:51:53 -0500 Subject: [PATCH 86/97] Too many languages to remember --- workers/arduino/arduino_worker.py | 6 +++--- workers/pi/lcd_worker.py | 11 ++++++----- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/workers/arduino/arduino_worker.py b/workers/arduino/arduino_worker.py index 9811d4a..f681fe3 100644 --- a/workers/arduino/arduino_worker.py +++ b/workers/arduino/arduino_worker.py @@ -32,6 +32,9 @@ def __init__(self, config, main_thread_running, system_ready, connection=None): self.relays = [] self.relayEvents = {} self.relay_index = 0 + + if connection is None: + self.connection = self.connect() try: if self.config['controls'] is not None: @@ -134,9 +137,6 @@ def resetConnection(self): def run(self): - if self.connection is None: - self.connection = self.connect() - for worker in self.workers: t = worker.run() self.threads.append(t) diff --git a/workers/pi/lcd_worker.py b/workers/pi/lcd_worker.py index 2cb14ab..38ea047 100644 --- a/workers/pi/lcd_worker.py +++ b/workers/pi/lcd_worker.py @@ -133,19 +133,20 @@ def work(self): self.pubsub.get_message() if self.lcd_available.is_set(): if self.cached_message and not need_new_message: - if self.current_message != self.cached_message.message: + if self.current_message != self.cached_message['message']: self.lcd.clear() - self.lcd.message = self.cached_message.message - self.current_message = self.cached_message.message # store message to only display once and prevent flickers - if self.elapsedTime() > self.cached_message.duration + 1: + self.lcd.message = self.cached_message['message'] + self.current_message = self.cached_message['message'] # store message to only display once and prevent flickers + if self.elapsedTime() > self.cached_message['duration ']+ 1: self.need_new_message = True else: # Get first time message after clear self.cached_message = self.nextMessageFromQueue() else: time.sleep(1) - except: + except Exception as e: print("LCD Worker \t\033[1;31m Unexpected Error\033[0;0m".format(**self.config)) + print(e) else: #System not ready time.sleep(1) From 2bcf868b152227d6fb434fbb88c9725d5152b7a7 Mon Sep 17 00:00:00 2001 From: Eric Davisson Date: Fri, 14 Aug 2020 15:12:54 -0500 Subject: [PATCH 87/97] Fixes to LCD worker --- workers/pi/lcd_worker.py | 2 +- workers/pi/sensor_worker.py | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/workers/pi/lcd_worker.py b/workers/pi/lcd_worker.py index 38ea047..384fe79 100644 --- a/workers/pi/lcd_worker.py +++ b/workers/pi/lcd_worker.py @@ -115,7 +115,7 @@ def addMessageToQueue(self, message, duration = 3): variables.r.publish(self.topic, json.dumps(msg)) return - def nextMessageFromQueue(): + def nextMessageFromQueue(self): if len(self.message_queue) > 0: self.need_new_message = False self.resetElapsedTime() diff --git a/workers/pi/sensor_worker.py b/workers/pi/sensor_worker.py index b5231d5..5c520a4 100644 --- a/workers/pi/sensor_worker.py +++ b/workers/pi/sensor_worker.py @@ -6,8 +6,6 @@ from .worker import Worker import sys sys.path.append('..') -from sensors.pi.float_sensor import (FloatSensor) -from sensors.pi.humidity_sensor import (HumiditySensor) import variables From 01ea03d064ddb03e949383c6e040e3ef6a996aed Mon Sep 17 00:00:00 2001 From: Eric Davisson Date: Fri, 14 Aug 2020 15:15:21 -0500 Subject: [PATCH 88/97] Added imports on sensor workers --- workers/arduino/arduino_sensor_worker.py | 8 +++++++- workers/pi/sensor_worker.py | 2 ++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/workers/arduino/arduino_sensor_worker.py b/workers/arduino/arduino_sensor_worker.py index d685041..7b3c2e6 100644 --- a/workers/arduino/arduino_sensor_worker.py +++ b/workers/arduino/arduino_sensor_worker.py @@ -3,10 +3,16 @@ import threading import random import socket +from .worker import Worker from nanpy import (SerialManager) from nanpy.serialmanager import SerialManagerError from nanpy.sockconnection import (SocketManager, SocketManagerError) -from .worker import Worker +from sensors.arduino.rain_sensor import (RainSensor) +from sensors.arduino.soil_sensor import (SoilSensor) +from sensors.arduino.float_sensor import (FloatSensor) +from sensors.arduino.light_sensor import (LightSensor) +from sensors.arduino.humidity_sensor import (HumiditySensor) +from sensors.arduino.temperature_sensor import (TemperatureSensor) import sys sys.path.append('..') diff --git a/workers/pi/sensor_worker.py b/workers/pi/sensor_worker.py index 5c520a4..b5231d5 100644 --- a/workers/pi/sensor_worker.py +++ b/workers/pi/sensor_worker.py @@ -6,6 +6,8 @@ from .worker import Worker import sys sys.path.append('..') +from sensors.pi.float_sensor import (FloatSensor) +from sensors.pi.humidity_sensor import (HumiditySensor) import variables From 6a6bbbff777fccf36972c833ffd21bb520a82b20 Mon Sep 17 00:00:00 2001 From: Eric Davisson Date: Fri, 14 Aug 2020 15:41:18 -0500 Subject: [PATCH 89/97] Updates to lcd worker --- mudpi.py | 2 +- workers/pi/lcd_worker.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/mudpi.py b/mudpi.py index 6577470..235fec2 100755 --- a/mudpi.py +++ b/mudpi.py @@ -201,7 +201,7 @@ print('MudPi Garden Controls...\t\t\033[1;32m Initialized\033[0;0m') - print('Signaling MudPi Controls...\t\t\033[1;32m Online\033[0;0m') + print('Engaging MudPi Workers...\t\t\033[1;32m \033[0;0m') for worker in workers: t = worker.run() threads.append(t) diff --git a/workers/pi/lcd_worker.py b/workers/pi/lcd_worker.py index 384fe79..30fa645 100644 --- a/workers/pi/lcd_worker.py +++ b/workers/pi/lcd_worker.py @@ -90,8 +90,8 @@ def handleMessage(self, message): try: if decoded_message['event'] == 'Message': if decoded_message.get('data', None): - self.addMessageToQueue(decoded_message['data'].get('data', ''), decoded_message['data'].get('duration', self.default_duration)) - print('LCD New Message: \033[1;36m{0}\033[0;0m'.format(decoded_message['data'])) + self.addMessageToQueue(decoded_message['data'].get('message', ''), decoded_message['data'].get('duration', self.default_duration)) + print('LCD Message Queued: \033[1;36m{0}\033[0;0m'.format(decoded_message['data'])) elif decoded_message['event'] == 'Clear': self.lcd.clear() print('Cleared the LCD Screen') From 0598eec06406d6fcccefff47d7b35ff9631b8ee0 Mon Sep 17 00:00:00 2001 From: Eric Davisson Date: Fri, 14 Aug 2020 15:49:01 -0500 Subject: [PATCH 90/97] Bug fix in LCD worker --- workers/pi/lcd_worker.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/workers/pi/lcd_worker.py b/workers/pi/lcd_worker.py index 30fa645..0f77212 100644 --- a/workers/pi/lcd_worker.py +++ b/workers/pi/lcd_worker.py @@ -132,7 +132,7 @@ def work(self): try: self.pubsub.get_message() if self.lcd_available.is_set(): - if self.cached_message and not need_new_message: + if self.cached_message and not self.need_new_message: if self.current_message != self.cached_message['message']: self.lcd.clear() self.lcd.message = self.cached_message['message'] From 17bad05fedaa40ecd244e0cd4d45c3fca78a2b35 Mon Sep 17 00:00:00 2001 From: Eric Davisson Date: Fri, 14 Aug 2020 16:23:48 -0500 Subject: [PATCH 91/97] Fixing lcd worker --- tools/lcd_message_tool.py | 2 +- workers/pi/lcd_worker.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/lcd_message_tool.py b/tools/lcd_message_tool.py index aefd82e..9e63f1c 100644 --- a/tools/lcd_message_tool.py +++ b/tools/lcd_message_tool.py @@ -38,7 +38,7 @@ def timedMessage(message, delay=3): "message":"", "duration":10 } - msg["message"] = int(input('Enter Message to Display: ')) + msg["message"] = str(input('Enter Message to Display: ')) msg["duration"] = int(input('Enter Duration to Display (seconds): ')) except: diff --git a/workers/pi/lcd_worker.py b/workers/pi/lcd_worker.py index 0f77212..f35a2c2 100644 --- a/workers/pi/lcd_worker.py +++ b/workers/pi/lcd_worker.py @@ -90,7 +90,7 @@ def handleMessage(self, message): try: if decoded_message['event'] == 'Message': if decoded_message.get('data', None): - self.addMessageToQueue(decoded_message['data'].get('message', ''), decoded_message['data'].get('duration', self.default_duration)) + self.addMessageToQueue(decoded_message['data'].get('message', ''), int(decoded_message['data'].get('duration', self.default_duration))) print('LCD Message Queued: \033[1;36m{0}\033[0;0m'.format(decoded_message['data'])) elif decoded_message['event'] == 'Clear': self.lcd.clear() @@ -137,7 +137,7 @@ def work(self): self.lcd.clear() self.lcd.message = self.cached_message['message'] self.current_message = self.cached_message['message'] # store message to only display once and prevent flickers - if self.elapsedTime() > self.cached_message['duration ']+ 1: + if self.elapsedTime() > self.cached_message['duration'] + 1: self.need_new_message = True else: # Get first time message after clear From d8a0956a2ca02edcd827d9f4cb6791934ea0d4dd Mon Sep 17 00:00:00 2001 From: Eric Davisson Date: Fri, 14 Aug 2020 16:51:09 -0500 Subject: [PATCH 92/97] Fixed LCD worker --- workers/pi/lcd_worker.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/workers/pi/lcd_worker.py b/workers/pi/lcd_worker.py index f35a2c2..a3c304f 100644 --- a/workers/pi/lcd_worker.py +++ b/workers/pi/lcd_worker.py @@ -42,7 +42,7 @@ def __init__(self, config, main_thread_running, system_ready, lcd_available): self.default_duration = 5 self.current_message = "" - self.cached_message = {} + self.cached_message = {'message':''} self.need_new_message = True self.message_queue = [] @@ -109,7 +109,7 @@ def addMessageToQueue(self, message, duration = 3): "message": message, "duration": duration } - self.message_queue.append(message) + self.message_queue.append(new_message) msg = { 'event':'MessageQueued', 'data': new_message } variables.r.publish(self.topic, json.dumps(msg)) From dc022e9868297f48ff054ffe7a6de4d3966bc61d Mon Sep 17 00:00:00 2001 From: Eric Davisson Date: Fri, 14 Aug 2020 19:46:56 -0500 Subject: [PATCH 93/97] Updates to LCD worker --- workers/pi/lcd_worker.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/workers/pi/lcd_worker.py b/workers/pi/lcd_worker.py index a3c304f..076b6f5 100644 --- a/workers/pi/lcd_worker.py +++ b/workers/pi/lcd_worker.py @@ -140,8 +140,9 @@ def work(self): if self.elapsedTime() > self.cached_message['duration'] + 1: self.need_new_message = True else: - # Get first time message after clear - self.cached_message = self.nextMessageFromQueue() + if self.need_new_message: + # Get first time message after clear + self.cached_message = self.nextMessageFromQueue() else: time.sleep(1) except Exception as e: From c697a37294c80301d48ee7fe9038e09d0360ca61 Mon Sep 17 00:00:00 2001 From: Eric Davisson Date: Fri, 14 Aug 2020 19:47:38 -0500 Subject: [PATCH 94/97] Logging update --- mudpi.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mudpi.py b/mudpi.py index 235fec2..617c4b6 100755 --- a/mudpi.py +++ b/mudpi.py @@ -186,7 +186,7 @@ raise Exception("Unknown Node Type: " + node['type']) nodes.append(t) except KeyError as e: - print('MudPi Node Workers...\t\t\033[1;31m Disabled\033[0;0m') + print('MudPi Node Workers...\t\t\t\033[1;31m Disabled\033[0;0m') try: if (CONFIGS['server'] is not None): From 183230aa34386329ff7b214d86bf20e41626efcb Mon Sep 17 00:00:00 2001 From: Eric Davisson Date: Fri, 14 Aug 2020 20:35:16 -0500 Subject: [PATCH 95/97] v0.9.0 Refactored redis configs Allow single redis instance across the app Allow redis to configured for remote connections v0.9.0 --- action.py | 7 +++++-- controls/arduino/button_control.py | 4 ++-- controls/arduino/control.py | 9 ++++++--- controls/arduino/potentiometer_control.py | 4 ++-- controls/arduino/switch_control.py | 4 ++-- controls/pi/button_control.py | 4 ++-- controls/pi/control.py | 9 ++++++--- controls/pi/switch_control.py | 4 ++-- mudpi.py | 23 ++++++++++++++++++----- package.json | 2 +- sensors/MCP3xxx/sensor.py | 7 ++++++- sensors/MCP3xxx/soil_sensor.py | 8 +++----- sensors/arduino/float_sensor.py | 6 +++--- sensors/arduino/humidity_sensor.py | 10 +++++----- sensors/arduino/light_sensor.py | 6 +++--- sensors/arduino/rain_sensor.py | 8 ++++---- sensors/arduino/sensor.py | 6 +++++- sensors/arduino/soil_sensor.py | 8 ++++---- sensors/arduino/temperature_sensor.py | 10 +++++----- sensors/pi/float_sensor.py | 4 ++-- sensors/pi/humidity_sensor.py | 12 +++++------- sensors/pi/i2c/bme680_sensor.py | 16 ++++++++-------- sensors/pi/i2c/sensor.py | 6 +++++- sensors/pi/sensor.py | 6 +++++- triggers/control_trigger.py | 9 ++++++--- triggers/sensor_trigger.py | 9 ++++++--- triggers/time_trigger.py | 1 - triggers/trigger.py | 1 - triggers/trigger_group.py | 1 - variables.py | 6 ------ workers/adc_worker.py | 9 ++++++--- workers/arduino/arduino_relay_worker.py | 12 ++++++------ workers/arduino/arduino_sensor_worker.py | 3 +-- workers/arduino/arduino_worker.py | 1 - workers/arduino/worker.py | 6 ++++-- workers/pi/camera_worker.py | 8 +++----- workers/pi/control_worker.py | 2 -- workers/pi/i2c_worker.py | 8 ++------ workers/pi/lcd_worker.py | 6 ++---- workers/pi/relay_worker.py | 14 ++++++-------- workers/pi/sensor_worker.py | 7 ++----- workers/pi/worker.py | 4 ++++ 42 files changed, 157 insertions(+), 133 deletions(-) diff --git a/action.py b/action.py index 357e9ef..865855a 100644 --- a/action.py +++ b/action.py @@ -4,7 +4,6 @@ import subprocess import sys sys.path.append('..') -import variables class Action(): @@ -15,6 +14,10 @@ def __init__(self, config): self.key = config.get("key", None).replace(" ", "_").lower() if config.get("key") is not None else self.name.replace(" ", "_").lower() # Actions will be either objects to publish for events or a command string to execute self.action = config.get("action") + try: + self.r = config["redis"] if config["redis"] is not None else redis.Redis(host='127.0.0.1', port=6379) + except KeyError: + self.r = redis.Redis(host='127.0.0.1', port=6379) return def init_action(self): @@ -31,7 +34,7 @@ def trigger(self, value=None): return def emitEvent(self): - variables.r.publish(self.topic, json.dumps(self.action)) + self.r.publish(self.topic, json.dumps(self.action)) return def runCommand(self, value=None): diff --git a/controls/arduino/button_control.py b/controls/arduino/button_control.py index 622de30..838a7c1 100644 --- a/controls/arduino/button_control.py +++ b/controls/arduino/button_control.py @@ -10,8 +10,8 @@ class ButtonControl(Control): - def __init__(self, pin, name='ButtonControl', key=None, connection=default_connection, analog_pin_mode=False, topic=None): - super().__init__(pin, name=name, key=key, connection=connection, analog_pin_mode=analog_pin_mode) + def __init__(self, pin, name='ButtonControl', key=None, connection=default_connection, analog_pin_mode=False, topic=None, redis_conn=None): + super().__init__(pin, name=name, key=key, connection=connection, analog_pin_mode=analog_pin_mode, redis_conn=redis_conn) self.topic = topic.replace(" ", "/").lower() if topic is not None else 'mudpi/relay/' self.state_counter = 3 self.previous_state = 0 diff --git a/controls/arduino/control.py b/controls/arduino/control.py index 6731ace..90a1341 100644 --- a/controls/arduino/control.py +++ b/controls/arduino/control.py @@ -4,20 +4,23 @@ from nanpy import (ArduinoApi, SerialManager) import sys sys.path.append('..') -import variables default_connection = SerialManager() # Base sensor class to extend all other arduino sensors from. class Control(): - def __init__(self, pin, name='Control', connection=default_connection, analog_pin_mode=False, key=None): + def __init__(self, pin, name='Control', connection=default_connection, analog_pin_mode=False, key=None, redis_conn=None): self.pin = pin self.name = name self.key = key.replace(" ", "_").lower() if key is not None else self.name.replace(" ", "_").lower() self.analog_pin_mode = analog_pin_mode self.connection = connection self.api = ArduinoApi(connection) + try: + self.r = redis_conn if redis_conn is not None else redis.Redis(host='127.0.0.1', port=6379) + except KeyError: + self.r = redis.Redis(host='127.0.0.1', port=6379) return def init_control(self): @@ -46,4 +49,4 @@ def emitEvent(self, data): } } print(message["data"]) - variables.r.publish('controls', json.dumps(message)) \ No newline at end of file + self.r.publish('controls', json.dumps(message)) \ No newline at end of file diff --git a/controls/arduino/potentiometer_control.py b/controls/arduino/potentiometer_control.py index dfe2f7b..29831ba 100644 --- a/controls/arduino/potentiometer_control.py +++ b/controls/arduino/potentiometer_control.py @@ -10,8 +10,8 @@ class PotentiometerControl(Control): - def __init__(self, pin, name='PotentiometerControl', key=None, connection=default_connection, analog_pin_mode=True, topic=None, reading_buffer=3): - super().__init__(pin, name=name, key=key, connection=connection, analog_pin_mode=analog_pin_mode) + def __init__(self, pin, name='PotentiometerControl', key=None, connection=default_connection, analog_pin_mode=True, topic=None, reading_buffer=3, redis_conn=None): + super().__init__(pin, name=name, key=key, connection=connection, analog_pin_mode=analog_pin_mode, redis_conn=redis_conn) self.previous_state = 0 # Reading buffer helps prevent multiple events when values are floating between small amounts self.reading_buffer = reading_buffer diff --git a/controls/arduino/switch_control.py b/controls/arduino/switch_control.py index b6ce966..0de49b4 100644 --- a/controls/arduino/switch_control.py +++ b/controls/arduino/switch_control.py @@ -10,8 +10,8 @@ class SwitchControl(Control): - def __init__(self, pin, name='SwitchControl', key=None, connection=default_connection, analog_pin_mode=False, topic=None): - super().__init__(pin, name=name, key=key, connection=connection, analog_pin_mode=analog_pin_mode) + def __init__(self, pin, name='SwitchControl', key=None, connection=default_connection, analog_pin_mode=False, topic=None, redis_conn=None): + super().__init__(pin, name=name, key=key, connection=connection, analog_pin_mode=analog_pin_mode, redis_conn=redis_conn) self.topic = topic.replace(" ", "/").lower() if topic is not None else 'mudpi/relay/' self.state_counter = 3 self.previous_state = 0 diff --git a/controls/pi/button_control.py b/controls/pi/button_control.py index eaa04d3..36471ea 100644 --- a/controls/pi/button_control.py +++ b/controls/pi/button_control.py @@ -9,8 +9,8 @@ class ButtonControl(Control): - def __init__(self, pin, name='ButtonControl', key=None, resistor=None, edge_detection=None, debounce=None, topic=None): - super().__init__(pin, name=name, key=key, resistor=resistor, edge_detection=edge_detection, debounce=debounce) + def __init__(self, pin, name='ButtonControl', key=None, resistor=None, edge_detection=None, debounce=None, topic=None, redis_conn=None): + super().__init__(pin, name=name, key=key, resistor=resistor, edge_detection=edge_detection, debounce=debounce, redis_conn=redis_conn) self.topic = topic.replace(" ", "/").lower() if topic is not None else 'mudpi/relay/' return diff --git a/controls/pi/control.py b/controls/pi/control.py index a9d0665..9a919bb 100644 --- a/controls/pi/control.py +++ b/controls/pi/control.py @@ -4,17 +4,20 @@ import RPi.GPIO as GPIO import sys sys.path.append('..') -import variables # Base sensor class to extend all other arduino sensors from. class Control(): - def __init__(self, pin, name='Control',key=None, resistor=None, edge_detection=None, debounce=None): + def __init__(self, pin, name='Control',key=None, resistor=None, edge_detection=None, debounce=None, redis_conn=None): self.pin = pin self.name = name self.key = key.replace(" ", "_").lower() if key is not None else self.name.replace(" ", "_").lower() self.gpio = GPIO self.debounce = debounce if debounce is not None else None + try: + self.r = redis_conn if redis_conn is not None else redis.Redis(host='127.0.0.1', port=6379) + except KeyError: + self.r = redis.Redis(host='127.0.0.1', port=6379) if resistor is not None: if resistor == "up" or resistor == GPIO.PUD_UP: @@ -67,4 +70,4 @@ def emitEvent(self, data): } } print(message["data"]) - variables.r.publish('controls', json.dumps(message)) \ No newline at end of file + self.r.publish('controls', json.dumps(message)) \ No newline at end of file diff --git a/controls/pi/switch_control.py b/controls/pi/switch_control.py index 968565a..a5e1397 100644 --- a/controls/pi/switch_control.py +++ b/controls/pi/switch_control.py @@ -9,8 +9,8 @@ class SwitchControl(Control): - def __init__(self, pin, name='SwitchControl', key=None, resistor=None, edge_detection=None, debounce=None, topic=None): - super().__init__(pin, name=name, key=key, resistor=resistor, edge_detection=edge_detection, debounce=debounce) + def __init__(self, pin, name='SwitchControl', key=None, resistor=None, edge_detection=None, debounce=None, topic=None, redis_conn=redis_conn): + super().__init__(pin, name=name, key=key, resistor=resistor, edge_detection=edge_detection, debounce=debounce, redis_conn=redis_conn) self.topic = topic.replace(" ", "/").lower() if topic is not None else 'mudpi/relay/' # Keep counter 1 above delay to avoid event on boot self.state_counter = 3 diff --git a/mudpi.py b/mudpi.py index 617c4b6..bfb0778 100755 --- a/mudpi.py +++ b/mudpi.py @@ -2,6 +2,7 @@ import threading import datetime import socket +import redis import time import json import sys @@ -48,6 +49,11 @@ print(chr(27) + "[2J") print('Loading MudPi Configs...\r', end="", flush=True) CONFIGS = loadConfigJson() +# Singleton redis to prevent connection conflicts +try: + r = redis.Redis(host=CONFIGS['redis'].get('host', '127.0.0.1'), port=int(CONFIGS['redis'].get('port', 6379))) +except KeyError: + r = redis.Redis(host='127.0.0.1', port=6379) # Waiting for redis and services to be running time.sleep(5) print('Loading MudPi Configs...\t\033[1;32m Complete\033[0;0m') @@ -64,7 +70,7 @@ print('') print('Eric Davisson @theDavisson') print('https://mudpi.app') -print('Version: ', CONFIGS.get('version', '0.8.14')) +print('Version: ', CONFIGS.get('version', '0.9.0')) print('\033[0;0m') if CONFIGS['debug'] is True: @@ -97,6 +103,7 @@ # Worker for Camera try: + CONFIGS["camera"]["redis"] = r c = CameraWorker(CONFIGS['camera'], main_thread_running, system_ready, camera_available) print('MudPi Camera...\t\t\t\033[1;32m Initializing\033[0;0m') workers.append(c) @@ -108,6 +115,7 @@ try: for worker in CONFIGS['workers']: # Create worker for worker + worker["redis"] = r if worker['type'] == "sensor": pw = PiSensorWorker(worker, main_thread_running, system_ready) print('MudPi Sensors...\t\t\t\033[1;32m Initializing\033[0;0m') @@ -119,6 +127,7 @@ print('MudPi I2C...\t\t\t\t\033[1;32m Initializing\033[0;0m') elif worker['type'] == "lcd": for lcd in worker['lcds']: + lcd["redis"] = r pw = LcdWorker(lcd, main_thread_running, system_ready, lcd_available) lcd_available.set() print('MudPi LCD Displays...\t\t\t\033[1;32m Initializing\033[0;0m') @@ -135,13 +144,14 @@ # Worker for relays attached to pi try: for relay in CONFIGS['relays']: + relay["redis"] = r relayState = { "available": threading.Event(), # Event to allow relay to activate "active": threading.Event() # Event to signal relay to open/close } relayEvents[relay.get("key", relay_index)] = relayState - r = RelayWorker(relay, main_thread_running, system_ready, relayState['available'], relayState['active']) - workers.append(r) + rw = RelayWorker(relay, main_thread_running, system_ready, relayState['available'], relayState['active']) + workers.append(rw) # Make the relays available, this event is toggled off elsewhere if we need to disable relays relayState['available'].set() relay_index +=1 @@ -152,6 +162,7 @@ try: for action in CONFIGS["actions"]: print('MudPi Actions...\t\t\t\033[1;32m Initializing\033[0;0m') + action["redis"] = r a = Action(action) a.init_action() actions[a.key] = a @@ -160,6 +171,7 @@ # Worker for Triggers try: + CONFIGS["triggers"]["redis"] = r t = TriggerWorker(CONFIGS['triggers'], main_thread_running, system_ready, actions) print('MudPi Triggers...\t\t\t\033[1;32m Initializing\033[0;0m') workers.append(t) @@ -170,6 +182,7 @@ # Supported nodes: arduinos, esp8266, ADC-MCP3xxx, probably others (esp32 with custom nanpy fork) try: for node in CONFIGS['nodes']: + node["redis"] = r if node['type'] == "arduino": if NANPY_ENABLED: print('MudPi Arduino Workers...\t\t\033[1;32m Initializing\033[0;0m') @@ -215,9 +228,9 @@ print('MudPi Garden Control...\t\t\t\033[1;32m Online\033[0;0m') print('_________________________________________________') system_ready.set() #Workers will not process until system is ready - variables.r.set('started_at', str(datetime.datetime.now())) #Store current time to track uptime + r.set('started_at', str(datetime.datetime.now())) #Store current time to track uptime system_message = {'event':'SystemStarted', 'data':1} - variables.r.publish('mudpi', json.dumps(system_message)) + r.publish('mudpi', json.dumps(system_message)) # Hold the program here until its time to graceful shutdown while PROGRAM_RUNNING: diff --git a/package.json b/package.json index 7cd542e..58ca662 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "mudpi-core", - "version": "0.8.14", + "version": "0.9.0", "description": "Configurable automated smart garden for raspberry pi", "bugs": "https://github.com/mudpi/mudpi-core/issues", "contributors": [ diff --git a/sensors/MCP3xxx/sensor.py b/sensors/MCP3xxx/sensor.py index 973abaf..3da291a 100644 --- a/sensors/MCP3xxx/sensor.py +++ b/sensors/MCP3xxx/sensor.py @@ -1,4 +1,5 @@ import adafruit_mcp3xxx.mcp3008 as MCP +import redis # Base sensor class to extend all other mcp3xxx sensors from. @@ -15,7 +16,7 @@ class Sensor: 7: MCP.P7, } - def __init__(self, pin: int, mcp, name='Sensor', key=None): + def __init__(self, pin: int, mcp, name='Sensor', key=None, redis_conn=None): self.pin = pin self.mcp = mcp self.topic = None @@ -23,6 +24,10 @@ def __init__(self, pin: int, mcp, name='Sensor', key=None): self.name = name self.key = key.replace(" ", "_").lower() if key is not None else self.name\ .replace(" ", str(pin)) + try: + self.r = redis_conn if redis_conn is not None else redis.Redis(host='127.0.0.1', port=6379) + except KeyError: + self.r = redis.Redis(host='127.0.0.1', port=6379) def init_sensor(self): """ diff --git a/sensors/MCP3xxx/soil_sensor.py b/sensors/MCP3xxx/soil_sensor.py index 9a1822e..7b5e67e 100644 --- a/sensors/MCP3xxx/soil_sensor.py +++ b/sensors/MCP3xxx/soil_sensor.py @@ -8,8 +8,6 @@ sys.path.append('..') -import variables - # Tested using Sun3Drucker Model SX239 # Wet Water = 287 # Dry Air = 584 @@ -20,8 +18,8 @@ class SoilSensor(Sensor): - def __init__(self, pin, mcp, name='SoilSensor', key=None): - super().__init__(pin, name=name, key=key, mcp=mcp) + def __init__(self, pin, mcp, name='SoilSensor', key=None, redis_conn=None): + super().__init__(pin, name=name, key=key, mcp=mcp, redis_conn=redis_conn) return def init_sensor(self): @@ -40,7 +38,7 @@ def read(self): moisture = 'Very Wet - ' + str(int(moistpercent)) # print("Resistance: %d" % resistance) # TODO: Put redis store into sensor worker - variables.r.set(self.key, + self.r.set(self.key, resistance) # TODO: CHANGE BACK TO 'moistpercent' (PERSONAL CONFIG) print("moisture: {0}".format(moisture)) diff --git a/sensors/arduino/float_sensor.py b/sensors/arduino/float_sensor.py index 8f7df6c..3311912 100644 --- a/sensors/arduino/float_sensor.py +++ b/sensors/arduino/float_sensor.py @@ -14,8 +14,8 @@ class FloatSensor(Sensor): - def __init__(self, pin, name='FloatSensor', key=None, connection=default_connection): - super().__init__(pin, name=name, key=key, connection=connection) + def __init__(self, pin, name='FloatSensor', key=None, connection=default_connection, redis_conn=None): + super().__init__(pin, name=name, key=key, connection=connection, redis_conn=redis_conn) return def init_sensor(self): @@ -24,7 +24,7 @@ def init_sensor(self): def read(self): value = self.api.digitalRead(self.pin) - variables.r.set(self.key, value) + self.r.set(self.key, value) return value def readRaw(self): diff --git a/sensors/arduino/humidity_sensor.py b/sensors/arduino/humidity_sensor.py index dde1dcb..9bf5291 100644 --- a/sensors/arduino/humidity_sensor.py +++ b/sensors/arduino/humidity_sensor.py @@ -15,8 +15,8 @@ class HumiditySensor(Sensor): - def __init__(self, pin, name='HumiditySensor', key=None, connection=default_connection, model='11', api=None): - super().__init__(pin, name=name, key=key, connection=connection) + def __init__(self, pin, name='HumiditySensor', key=None, connection=default_connection, model='11', api=None, redis_conn=None): + super().__init__(pin, name=name, key=key, connection=connection, redis_conn=redis_conn) self.type = model #DHT11 or DHT22 maybe AM2302 return @@ -37,9 +37,9 @@ def read(self): temperature = self.dht.readTemperature(True) humidity = self.dht.readHumidity() data = {'temperature': round(temperature, 2), 'humidity': round(humidity, 2)} - variables.r.set(self.key + '_temperature', temperature) - variables.r.set(self.key + '_humidity', humidity) - variables.r.set(self.key, json.dumps(data)) + self.r.set(self.key + '_temperature', temperature) + self.r.set(self.key + '_humidity', humidity) + self.r.set(self.key, json.dumps(data)) return data def readRaw(self): diff --git a/sensors/arduino/light_sensor.py b/sensors/arduino/light_sensor.py index 7bfd19a..96cc8ff 100644 --- a/sensors/arduino/light_sensor.py +++ b/sensors/arduino/light_sensor.py @@ -14,8 +14,8 @@ class LightSensor(Sensor): - def __init__(self, pin, name='LightSensor', key=None, connection=default_connection): - super().__init__(pin, name=name, key=key, connection=connection) + def __init__(self, pin, name='LightSensor', key=None, connection=default_connection, redis_conn=None): + super().__init__(pin, name=name, key=key, connection=connection, redis_conn=redis_conn) return def init_sensor(self): @@ -24,7 +24,7 @@ def init_sensor(self): def read(self): light_intesity = self.api.analogRead(self.pin) - r.set(self.key, light_intesity) + self.r.set(self.key, light_intesity) return light_intesity def readRaw(self): diff --git a/sensors/arduino/rain_sensor.py b/sensors/arduino/rain_sensor.py index 73f04c3..2257758 100644 --- a/sensors/arduino/rain_sensor.py +++ b/sensors/arduino/rain_sensor.py @@ -21,8 +21,8 @@ class RainSensor(Sensor): - def __init__(self, pin, name='RainSensor', key=None, connection=default_connection): - super().__init__(pin, name=name, key=key, connection=connection) + def __init__(self, pin, name='RainSensor', key=None, connection=default_connection, redis_conn=None): + super().__init__(pin, name=name, key=key, connection=connection, redis_conn=redis_conn) return def init_sensor(self): @@ -32,13 +32,13 @@ def init_sensor(self): def read(self): rain = self.api.analogRead(self.pin) #TODO: REMOVE (PERSONAL CONFIG) #rain = self.parseSensorReading(self.api.analogRead(self.pin)) - variables.r.set(self.key, rain) + self.r.set(self.key, rain) return rain def readRaw(self): resistance = self.api.analogRead(self.pin) #print("Resistance: %d" % resistance) - variables.r.set(self.key+'_raw', resistance) + self.r.set(self.key+'_raw', resistance) return resistance def parseSensorReading(self, raw_data): diff --git a/sensors/arduino/sensor.py b/sensors/arduino/sensor.py index 9fa174b..e6fd3d4 100644 --- a/sensors/arduino/sensor.py +++ b/sensors/arduino/sensor.py @@ -8,13 +8,17 @@ # Base sensor class to extend all other arduino sensors from. class Sensor(): - def __init__(self, pin, name='Sensor', connection=default_connection, analog_pin_mode=False, key=None, api=None): + def __init__(self, pin, name='Sensor', connection=default_connection, analog_pin_mode=False, key=None, api=None, redis_conn=None): self.pin = pin self.name = name self.key = key.replace(" ", "_").lower() if key is not None else self.name.replace(" ", "_").lower() self.analog_pin_mode = analog_pin_mode self.connection = connection self.api = api if api is not None else ArduinoApi(connection) + try: + self.r = redis_conn if redis_conn is not None else redis.Redis(host='127.0.0.1', port=6379) + except KeyError: + self.r = redis.Redis(host='127.0.0.1', port=6379) return def init_sensor(self): diff --git a/sensors/arduino/soil_sensor.py b/sensors/arduino/soil_sensor.py index 4b1be2b..dcd6f40 100644 --- a/sensors/arduino/soil_sensor.py +++ b/sensors/arduino/soil_sensor.py @@ -19,8 +19,8 @@ intervals = int((AirBounds - WaterBounds)/3); class SoilSensor(Sensor): - def __init__(self, pin, name='SoilSensor', key=None, connection=default_connection): - super().__init__(pin, name=name, key=key, connection=connection) + def __init__(self, pin, name='SoilSensor', key=None, connection=default_connection, redis_conn=None): + super().__init__(pin, name=name, key=key, connection=connection, redis_conn=redis_conn) return def init_sensor(self): @@ -40,11 +40,11 @@ def read(self): moisture = 'Very Wet - ' + str(int(moistpercent)) #print("Resistance: %d" % resistance) #TODO: Put redis store into sensor worker - variables.r.set(self.key, resistance) #TODO: CHANGE BACK TO 'moistpercent' (PERSONAL CONFIG) + self.r.set(self.key, resistance) #TODO: CHANGE BACK TO 'moistpercent' (PERSONAL CONFIG) return resistance def readRaw(self): resistance = self.api.analogRead(self.pin) #print("Resistance: %d" % resistance) - variables.r.set(self.key+'_raw', resistance) + self.r.set(self.key+'_raw', resistance) return resistance \ No newline at end of file diff --git a/sensors/arduino/temperature_sensor.py b/sensors/arduino/temperature_sensor.py index ff16537..2175264 100644 --- a/sensors/arduino/temperature_sensor.py +++ b/sensors/arduino/temperature_sensor.py @@ -14,8 +14,8 @@ class TemperatureSensor(Sensor): - def __init__(self, pin, name='TemperatureHumiditySensor', key=None, connection=default_connection): - super().__init__(pin, name=name, key=key, connection=connection) + def __init__(self, pin, name='TemperatureHumiditySensor', key=None, connection=default_connection, redis_conn=None): + super().__init__(pin, name=name, key=key, connection=connection, redis_conn=redis_conn) return def init_sensor(self): @@ -35,7 +35,7 @@ def init_sensor(self): #sensor = id of sensor you want in addresses[] def read(self): #temp = self.sensors.getTempF(sensor) - #variables.r.set('temp_'+str(sensor), temp) + #self.r.set('temp_'+str(sensor), temp) #return temp return self.readAll() @@ -45,10 +45,10 @@ def readAll(self): for i in range(self.sensor_bus): temp = self.sensors.getTempC(i) temps['temp_'+str(i)] = temp - #variables.r.set(self.key+'_'+str(i), temp) + #self.r.set(self.key+'_'+str(i), temp) #print("Device %d (%s) " % (i, self.addresses[i])) #print("Let's convert it in Fahrenheit degrees: %0.2f" % DallasTemperature.toFahrenheit(temp)) - variables.r.set(self.key, temps) + self.r.set(self.key, temps) return temps if __name__ == '__main__': diff --git a/sensors/pi/float_sensor.py b/sensors/pi/float_sensor.py index b05bea4..6762a6a 100644 --- a/sensors/pi/float_sensor.py +++ b/sensors/pi/float_sensor.py @@ -8,8 +8,8 @@ class FloatSensor(Sensor): - def __init__(self, pin, name='FloatSensor', key=None): - super().__init__(pin, name=name, key=key) + def __init__(self, pin, name='FloatSensor', key=None, redis_conn=None): + super().__init__(pin, name=name, key=key, redis_conn=redis_conn) return def init_sensor(self): diff --git a/sensors/pi/humidity_sensor.py b/sensors/pi/humidity_sensor.py index 1317b72..05a95ec 100644 --- a/sensors/pi/humidity_sensor.py +++ b/sensors/pi/humidity_sensor.py @@ -7,15 +7,13 @@ import sys sys.path.append('..') -import variables - #r = redis.Redis(host='127.0.0.1', port=6379) #PIN MODE : OUT | IN class HumiditySensor(Sensor): - def __init__(self, pin, name='HumdityTempSensor', key=None, model='11'): - super().__init__(pin, name=name, key=key) + def __init__(self, pin, name='HumdityTempSensor', key=None, model='11', redis_conn=None): + super().__init__(pin, name=name, key=key, redis_conn=redis_conn) self.type = model return @@ -37,10 +35,10 @@ def read(self): humidity, temperature = Adafruit_DHT.read_retry(self.sensor, self.pin) if humidity is not None and temperature is not None: - variables.r.set(self.key + '_temperature', round(temperature * 1.8 + 32, 2)) - variables.r.set(self.key + '_humidity', humidity) + self.r.set(self.key + '_temperature', round(temperature * 1.8 + 32, 2)) + self.r.set(self.key + '_humidity', humidity) readings = {'temperature': round(temperature * 1.8 + 32, 2), 'humidity': round(humidity, 2)} - variables.r.set(self.key, json.dumps(readings)) + self.r.set(self.key, json.dumps(readings)) return readings else: print('Failed to get DHT reading. Try again!') diff --git a/sensors/pi/i2c/bme680_sensor.py b/sensors/pi/i2c/bme680_sensor.py index 474fcd6..1af9be6 100644 --- a/sensors/pi/i2c/bme680_sensor.py +++ b/sensors/pi/i2c/bme680_sensor.py @@ -14,8 +14,8 @@ class Bme680Sensor(Sensor): - def __init__(self, address = None, name='PressureSensor', key=None): - super().__init__(address, name=name, key=key) + def __init__(self, address = None, name='PressureSensor', key=None, redis_conn=None): + super().__init__(address, name=name, key=key, redis_conn=redis_conn) return def init_sensor(self): @@ -32,13 +32,13 @@ def read(self): altitude = round(self.sensor.altitude, 3) if humidity is not None and temperature is not None: - variables.r.set(self.key + '_temperature', temperature) - variables.r.set(self.key + '_humidity', humidity) - variables.r.set(self.key + '_gas', gas) - variables.r.set(self.key + '_pressure', pressure) - variables.r.set(self.key + '_altitude', altitude) + self.r.set(self.key + '_temperature', temperature) + self.r.set(self.key + '_humidity', humidity) + self.r.set(self.key + '_gas', gas) + self.r.set(self.key + '_pressure', pressure) + self.r.set(self.key + '_altitude', altitude) readings = {'temperature': temperature, 'humidity': humidity, 'pressure': pressure, 'gas': gas, 'altitude': altitude} - variables.r.set(self.key, json.dumps(readings)) + self.r.set(self.key, json.dumps(readings)) # print('BME680:', readings) return readings else: diff --git a/sensors/pi/i2c/sensor.py b/sensors/pi/i2c/sensor.py index bcd2df5..7275fc2 100644 --- a/sensors/pi/i2c/sensor.py +++ b/sensors/pi/i2c/sensor.py @@ -9,12 +9,16 @@ class Sensor(): - def __init__(self, address, name='Sensor', key=None): + def __init__(self, address, name='Sensor', key=None, redis_conn=None): self.address = address self.name = name self.key = key.replace(" ", "_").lower() if key is not None else self.name.replace(" ", "_").lower() self.gpio = GPIO self.i2c = I2C(board.SCL, board.SDA) + try: + self.r = redis_conn if redis_conn is not None else redis.Redis(host='127.0.0.1', port=6379) + except KeyError: + self.r = redis.Redis(host='127.0.0.1', port=6379) return def init_sensor(self): diff --git a/sensors/pi/sensor.py b/sensors/pi/sensor.py index 15b0da7..5cc813e 100644 --- a/sensors/pi/sensor.py +++ b/sensors/pi/sensor.py @@ -7,11 +7,15 @@ class Sensor(): - def __init__(self, pin, name='Sensor', key=None): + def __init__(self, pin, name='Sensor', key=None, redis_conn=None): self.pin = pin self.name = name self.key = key.replace(" ", "_").lower() if key is not None else self.name.replace(" ", "_").lower() self.gpio = GPIO + try: + self.r = redis_conn if redis_conn is not None else redis.Redis(host='127.0.0.1', port=6379) + except KeyError: + self.r = redis.Redis(host='127.0.0.1', port=6379) return def init_sensor(self): diff --git a/triggers/control_trigger.py b/triggers/control_trigger.py index f4437fd..95acdfc 100644 --- a/triggers/control_trigger.py +++ b/triggers/control_trigger.py @@ -4,19 +4,22 @@ import sys from .trigger import Trigger sys.path.append('..') -import variables class ControlTrigger(Trigger): - def __init__(self, main_thread_running, system_ready, name='ControlTrigger',key=None, source=None, thresholds=None, topic="controls", trigger_active=None, frequency='once', actions=[], group=None): + def __init__(self, main_thread_running, system_ready, name='ControlTrigger',key=None, source=None, thresholds=None, topic="controls", trigger_active=None, frequency='once', actions=[], group=None, redis_conn=None): super().__init__(main_thread_running, system_ready, name=name, key=key, source=source, thresholds=thresholds, trigger_active=trigger_active, frequency=frequency, actions=actions, trigger_interval=0.5, group=group) self.topic = topic.replace(" ", "_").lower() if topic is not None else "controls" + try: + self.r = redis_conn if redis_conn is not None else redis.Redis(host='127.0.0.1', port=6379) + except KeyError: + self.r = redis.Redis(host='127.0.0.1', port=6379) return def init_trigger(self): #Initialize the trigger here (i.e. set listeners or create cron jobs) #Pubsub Listeners - self.pubsub = variables.r.pubsub() + self.pubsub = self.r.pubsub() self.pubsub.subscribe(**{self.topic: self.handleEvent}) pass diff --git a/triggers/sensor_trigger.py b/triggers/sensor_trigger.py index 93d85ff..21a7d38 100644 --- a/triggers/sensor_trigger.py +++ b/triggers/sensor_trigger.py @@ -4,20 +4,23 @@ import sys from .trigger import Trigger sys.path.append('..') -import variables class SensorTrigger(Trigger): - def __init__(self, main_thread_running, system_ready, name='SensorTrigger',key=None, source=None, nested_source=None, thresholds=None, topic="sensors", trigger_active=None, frequency='once', actions=[], group=None): + def __init__(self, main_thread_running, system_ready, name='SensorTrigger',key=None, source=None, nested_source=None, thresholds=None, topic="sensors", trigger_active=None, frequency='once', actions=[], group=None, redis_conn=None): super().__init__(main_thread_running, system_ready, name=name, key=key, source=source, thresholds=thresholds, trigger_active=trigger_active, frequency=frequency, actions=actions, trigger_interval=0.5, group=group) self.topic = topic.replace(" ", "_").lower() if topic is not None else "sensors" self.nested_source = nested_source.lower() if nested_source is not None else nested_source + try: + self.r = redis_conn if redis_conn is not None else redis.Redis(host='127.0.0.1', port=6379) + except KeyError: + self.r = redis.Redis(host='127.0.0.1', port=6379) return def init_trigger(self): #Initialize the trigger here (i.e. set listeners or create cron jobs) #Pubsub Listeners - self.pubsub = variables.r.pubsub() + self.pubsub = self.r.pubsub() self.pubsub.subscribe(**{self.topic: self.handleEvent}) pass diff --git a/triggers/time_trigger.py b/triggers/time_trigger.py index 48629df..0a50c1c 100644 --- a/triggers/time_trigger.py +++ b/triggers/time_trigger.py @@ -9,7 +9,6 @@ except ImportError: CRON_ENABLED = False sys.path.append('..') -import variables class TimeTrigger(Trigger): diff --git a/triggers/trigger.py b/triggers/trigger.py index 9196ba3..2f9f1af 100644 --- a/triggers/trigger.py +++ b/triggers/trigger.py @@ -4,7 +4,6 @@ import threading import sys sys.path.append('..') -import variables class Trigger(): diff --git a/triggers/trigger_group.py b/triggers/trigger_group.py index 83ec9e7..b8a48e3 100644 --- a/triggers/trigger_group.py +++ b/triggers/trigger_group.py @@ -4,7 +4,6 @@ import threading import sys sys.path.append('..') -import variables class TriggerGroup(): diff --git a/variables.py b/variables.py index 28bf8ff..1388c2d 100644 --- a/variables.py +++ b/variables.py @@ -1,11 +1,5 @@ -import redis - -lcd_message = {'line_1': 'Temperature: ', 'line_2': 'Humidity: '} PREVIOUS_LINE="\x1b[1F" RED_BACK="\x1b[41;37m" GREEN_BACK="\x1b[42;30m" YELLOW_BACK="\x1b[43;30m" RESET="\x1b[0m" - -# Singleton redis to prevent connection conflicts -r = redis.Redis(host='127.0.0.1', port=6379) \ No newline at end of file diff --git a/workers/adc_worker.py b/workers/adc_worker.py index 87d3e95..736f83c 100644 --- a/workers/adc_worker.py +++ b/workers/adc_worker.py @@ -3,11 +3,10 @@ import board import adafruit_mcp3xxx.mcp3008 as MCP from adafruit_mcp3xxx.analog_in import AnalogIn - import threading -import variables import time import json +import redis import importlib @@ -40,6 +39,10 @@ def __init__(self, config: dict, main_thread_running, system_ready): self.main_thread_running = main_thread_running self.system_ready = system_ready self.node_ready = False + try: + self.r = redis_conn if redis_conn is not None else redis.Redis(host='127.0.0.1', port=6379) + except KeyError: + self.r = redis.Redis(host='127.0.0.1', port=6379) spi = busio.SPI(clock=board.SCK, MISO=board.MISO, MOSI=board.MOSI) cs = digitalio.DigitalInOut(ADCMCP3008Worker.PINS[config['pin']]) @@ -104,7 +107,7 @@ def work(self): print(readings) message['data'] = readings - variables.r.publish('sensors', json.dumps(message)) + self.r.publish('sensors', json.dumps(message)) time.sleep(15) # This is only ran after the main thread is shut down diff --git a/workers/arduino/arduino_relay_worker.py b/workers/arduino/arduino_relay_worker.py index 77c5f45..c5c07eb 100644 --- a/workers/arduino/arduino_relay_worker.py +++ b/workers/arduino/arduino_relay_worker.py @@ -31,7 +31,7 @@ def __init__(self, config, main_thread_running, system_ready, relay_available, r self.topic = self.config['topic'].replace(" ", "/").lower() if self.config['topic'] is not None else 'mudpi/relay/*' # Pubsub Listeners - self.pubsub = variables.r.pubsub() + self.pubsub = self.r.pubsub() self.pubsub.subscribe(**{self.topic: self.handleMessage}) self.api = api @@ -51,7 +51,7 @@ def init(self): #Feature to restore relay state in case of crash or unexpected shutdown. This will check for last state stored in redis and set relay accordingly if(self.config.get('restore_last_known_state', None) is not None and self.config.get('restore_last_known_state', False) is True): - if(variables.r.get(self.config['key']+'_state')): + if(self.r.get(self.config['key']+'_state')): self.api.digitalWrite(self.config['pin'], self.pin_state_on) print('Restoring Relay \033[1;36m{0} On\033[0;0m'.format(self.config['key'])) @@ -115,8 +115,8 @@ def turnOn(self): if not self.active: self.api.digitalWrite(self.config['pin'], self.pin_state_on) message = {'event':'StateChanged', 'data':1} - variables.r.set(self.config['key']+'_state', 1) - variables.r.publish(self.topic, json.dumps(message)) + self.r.set(self.config['key']+'_state', 1) + self.r.publish(self.topic, json.dumps(message)) self.active = True #self.relay_active.set() This is handled by the redis listener now self.resetElapsedTime() @@ -127,8 +127,8 @@ def turnOff(self): if self.active: self.api.digitalWrite(self.config['pin'], self.pin_state_off) message = {'event':'StateChanged', 'data':0} - variables.r.delete(self.config['key']+'_state') - variables.r.publish(self.topic, json.dumps(message)) + self.r.delete(self.config['key']+'_state') + self.r.publish(self.topic, json.dumps(message)) #self.relay_active.clear() This is handled by the redis listener now self.active = False self.resetElapsedTime() diff --git a/workers/arduino/arduino_sensor_worker.py b/workers/arduino/arduino_sensor_worker.py index 7b3c2e6..9e23630 100644 --- a/workers/arduino/arduino_sensor_worker.py +++ b/workers/arduino/arduino_sensor_worker.py @@ -16,7 +16,6 @@ import sys sys.path.append('..') -import variables import importlib #r = redis.Redis(host='127.0.0.1', port=6379) @@ -97,7 +96,7 @@ def work(self): print("Node Readings: ", readings) message['data'] = readings - variables.r.publish(self.topic, json.dumps(message)) + self.r.publish(self.topic, json.dumps(message)) except (SerialManagerError, SocketManagerError, BrokenPipeError, ConnectionResetError, OSError, socket.timeout) as e: print('\033[1;36m{name}\033[0;0m -> \033[1;33mSensors Timeout!\033[0;0m'.format(**self.config)) self.sensors = [] diff --git a/workers/arduino/arduino_worker.py b/workers/arduino/arduino_worker.py index f681fe3..72f350a 100644 --- a/workers/arduino/arduino_worker.py +++ b/workers/arduino/arduino_worker.py @@ -13,7 +13,6 @@ import sys sys.path.append('..') -import variables import importlib #r = redis.Redis(host='127.0.0.1', port=6379) diff --git a/workers/arduino/worker.py b/workers/arduino/worker.py index 71f0551..8ce513c 100644 --- a/workers/arduino/worker.py +++ b/workers/arduino/worker.py @@ -6,13 +6,15 @@ import sys sys.path.append('..') -import variables - # Base Worker Class # A worker is responsible for handling its set of operations and running on a thread class Worker(): def __init__(self, config, main_thread_running, system_ready): self.config = config + try: + self.r = config["redis"] + except KeyError: + self.r = redis.Redis(host='127.0.0.1', port=6379) self.topic = config.get('topic', 'mudpi').replace(" ", "_").lower() self.sleep_duration = config.get('sleep_duration', 15) diff --git a/workers/pi/camera_worker.py b/workers/pi/camera_worker.py index ce77c4a..b0a6f43 100644 --- a/workers/pi/camera_worker.py +++ b/workers/pi/camera_worker.py @@ -10,8 +10,6 @@ from .worker import Worker sys.path.append('..') -import variables - class CameraWorker(Worker): def __init__(self, config, main_thread_running, system_ready, camera_available): @@ -52,7 +50,7 @@ def init(self): self.camera = PiCamera() #Pubsub Listeners - self.pubsub = variables.r.pubsub() + self.pubsub = self.r.pubsub() self.pubsub.subscribe(**{self.topic: self.handleEvent}) print('Camera Worker...\t\t\t\033[1;32m Ready\033[0;0m') @@ -123,8 +121,8 @@ def work(self): print("Error During Camera Reset Cleanup") break; message = {'event':'StateChanged', 'data':filename} - variables.r.set('last_camera_image', filename) - variables.r.publish(self.topic, json.dumps(message)) + self.r.set('last_camera_image', filename) + self.r.publish(self.topic, json.dumps(message)) print('Image Captured \033[1;36m%s\033[0;0m' % filename) self.wait() # except: diff --git a/workers/pi/control_worker.py b/workers/pi/control_worker.py index 3a399fc..d725eed 100644 --- a/workers/pi/control_worker.py +++ b/workers/pi/control_worker.py @@ -9,8 +9,6 @@ from controls.pi.button_control import (ButtonControl) from controls.pi.switch_control import (SwitchControl) -import variables - class PiControlWorker(Worker): def __init__(self, config, main_thread_running, system_ready): super().__init__(config, main_thread_running, system_ready) diff --git a/workers/pi/i2c_worker.py b/workers/pi/i2c_worker.py index 4f473d5..46b4c1d 100644 --- a/workers/pi/i2c_worker.py +++ b/workers/pi/i2c_worker.py @@ -8,10 +8,6 @@ from .worker import Worker from sensors.pi.i2c.bme680_sensor import (Bme680Sensor) -import variables - -#r = redis.Redis(host='127.0.0.1', port=6379) -# def clamp(n, smallest, largest): return max(smallest, min(n, largest)) class PiI2CWorker(Worker): def __init__(self, config, main_thread_running, system_ready): @@ -67,11 +63,11 @@ def work(self): for sensor in self.sensors: result = sensor.read() readings[sensor.key] = result - variables.r.set(sensor.key, json.dumps(result)) + self.r.set(sensor.key, json.dumps(result)) message['data'] = readings print(readings); - variables.r.publish(self.topic, json.dumps(message)) + self.r.publish(self.topic, json.dumps(message)) time.sleep(self.sleep_duration) time.sleep(2) diff --git a/workers/pi/lcd_worker.py b/workers/pi/lcd_worker.py index 076b6f5..e7923c6 100644 --- a/workers/pi/lcd_worker.py +++ b/workers/pi/lcd_worker.py @@ -11,8 +11,6 @@ import sys sys.path.append('..') -import variables - class LcdWorker(Worker): def __init__(self, config, main_thread_running, system_ready, lcd_available): super().__init__(config, main_thread_running, system_ready) @@ -56,7 +54,7 @@ def __init__(self, config, main_thread_running, system_ready, lcd_available): self.topic = 'mudpi/lcd' # Pubsub Listeners - self.pubsub = variables.r.pubsub() + self.pubsub = self.r.pubsub() self.pubsub.subscribe(**{self.topic: self.handleMessage}) self.init() @@ -112,7 +110,7 @@ def addMessageToQueue(self, message, duration = 3): self.message_queue.append(new_message) msg = { 'event':'MessageQueued', 'data': new_message } - variables.r.publish(self.topic, json.dumps(msg)) + self.r.publish(self.topic, json.dumps(msg)) return def nextMessageFromQueue(self): diff --git a/workers/pi/relay_worker.py b/workers/pi/relay_worker.py index c16aafa..1a4227a 100644 --- a/workers/pi/relay_worker.py +++ b/workers/pi/relay_worker.py @@ -8,8 +8,6 @@ from .worker import Worker sys.path.append('..') -import variables - class RelayWorker(Worker): def __init__(self, config, main_thread_running, system_ready, relay_available, relay_active): super().__init__(config, main_thread_running, system_ready) @@ -26,7 +24,7 @@ def __init__(self, config, main_thread_running, system_ready, relay_available, r self.pin_state_on = GPIO.LOW if self.config['normally_open'] is not None and self.config['normally_open'] else GPIO.HIGH # Pubsub Listeners - self.pubsub = variables.r.pubsub() + self.pubsub = self.r.pubsub() self.pubsub.subscribe(**{self.topic: self.handleMessage}) self.init() @@ -40,7 +38,7 @@ def init(self): #Feature to restore relay state in case of crash or unexpected shutdown. This will check for last state stored in redis and set relay accordingly if(self.config.get('restore_last_known_state', None) is not None and self.config.get('restore_last_known_state', False) is True): - if(variables.r.get(self.config['key']+'_state')): + if(self.r.get(self.config['key']+'_state')): GPIO.output(self.config['pin'], self.pin_state_on) print('Restoring Relay \033[1;36m{0} On\033[0;0m'.format(self.config['key'])) @@ -79,8 +77,8 @@ def turnOn(self): if not self.active: GPIO.output(self.config['pin'], self.pin_state_on) message = {'event':'StateChanged', 'data':1} - variables.r.set(self.config['key']+'_state', 1) - variables.r.publish(self.topic, json.dumps(message)) + self.r.set(self.config['key']+'_state', 1) + self.r.publish(self.topic, json.dumps(message)) self.active = True #self.relay_active.set() This is handled by the redis listener now self.resetElapsedTime() @@ -91,8 +89,8 @@ def turnOff(self): if self.active: GPIO.output(self.config['pin'], self.pin_state_off) message = {'event':'StateChanged', 'data':0} - variables.r.delete(self.config['key']+'_state') - variables.r.publish(self.topic, json.dumps(message)) + self.r.delete(self.config['key']+'_state') + self.r.publish(self.topic, json.dumps(message)) #self.relay_active.clear() This is handled by the redis listener now self.active = False self.resetElapsedTime() diff --git a/workers/pi/sensor_worker.py b/workers/pi/sensor_worker.py index b5231d5..ea6c2a3 100644 --- a/workers/pi/sensor_worker.py +++ b/workers/pi/sensor_worker.py @@ -9,9 +9,6 @@ from sensors.pi.float_sensor import (FloatSensor) from sensors.pi.humidity_sensor import (HumiditySensor) -import variables - - class PiSensorWorker(Worker): def __init__(self, config, main_thread_running, system_ready): super().__init__(config, main_thread_running, system_ready) @@ -68,7 +65,7 @@ def work(self): for sensor in self.sensors: result = sensor.read() readings[sensor.key] = result - variables.r.set(sensor.key, json.dumps(result)) + self.r.set(sensor.key, json.dumps(result)) #print(sensor.name, result) #Check for a critical water level from any float sensors @@ -83,7 +80,7 @@ def work(self): print(readings) message['data'] = readings - variables.r.publish(self.topic, json.dumps(message)) + self.r.publish(self.topic, json.dumps(message)) time.sleep(self.sleep_duration) time.sleep(2) diff --git a/workers/pi/worker.py b/workers/pi/worker.py index 90e9bf0..aaaf517 100644 --- a/workers/pi/worker.py +++ b/workers/pi/worker.py @@ -19,6 +19,10 @@ def wrapper(*args, **kwargs): class Worker(): def __init__(self, config, main_thread_running, system_ready): self.config = config + try: + self.r = config["redis"] + except KeyError: + self.r = redis.Redis(host='127.0.0.1', port=6379) self.topic = config.get('topic', 'mudpi').replace(" ", "_").lower() self.sleep_duration = config.get('sleep_duration', 15) From bb02217f9826f369ee19e93b62b6d1db90cf7ef3 Mon Sep 17 00:00:00 2001 From: Eric Davisson Date: Fri, 14 Aug 2020 20:36:39 -0500 Subject: [PATCH 96/97] Fixed arg default --- controls/pi/switch_control.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/controls/pi/switch_control.py b/controls/pi/switch_control.py index a5e1397..180ed6b 100644 --- a/controls/pi/switch_control.py +++ b/controls/pi/switch_control.py @@ -9,7 +9,7 @@ class SwitchControl(Control): - def __init__(self, pin, name='SwitchControl', key=None, resistor=None, edge_detection=None, debounce=None, topic=None, redis_conn=redis_conn): + def __init__(self, pin, name='SwitchControl', key=None, resistor=None, edge_detection=None, debounce=None, topic=None, redis_conn=None): super().__init__(pin, name=name, key=key, resistor=resistor, edge_detection=edge_detection, debounce=debounce, redis_conn=redis_conn) self.topic = topic.replace(" ", "/").lower() if topic is not None else 'mudpi/relay/' # Keep counter 1 above delay to avoid event on boot From 4978177fecc515e77dba39af79ca74ed0472a9b7 Mon Sep 17 00:00:00 2001 From: Eric Davisson Date: Fri, 14 Aug 2020 20:40:50 -0500 Subject: [PATCH 97/97] Better checks for null configs --- mudpi.py | 142 +++++++++++++++++++++++++++++-------------------------- 1 file changed, 74 insertions(+), 68 deletions(-) diff --git a/mudpi.py b/mudpi.py index bfb0778..18ac9da 100755 --- a/mudpi.py +++ b/mudpi.py @@ -103,101 +103,107 @@ # Worker for Camera try: - CONFIGS["camera"]["redis"] = r - c = CameraWorker(CONFIGS['camera'], main_thread_running, system_ready, camera_available) - print('MudPi Camera...\t\t\t\033[1;32m Initializing\033[0;0m') - workers.append(c) - camera_available.set() + if len(CONFIGS["camera"]) > 0: + CONFIGS["camera"]["redis"] = r + c = CameraWorker(CONFIGS['camera'], main_thread_running, system_ready, camera_available) + print('MudPi Camera...\t\t\t\033[1;32m Initializing\033[0;0m') + workers.append(c) + camera_available.set() except KeyError: print('MudPi Pi Camera...\t\t\t\033[1;31m Disabled\033[0;0m') # Workers for pi (Sensors, Controls, Relays, I2C) try: - for worker in CONFIGS['workers']: - # Create worker for worker - worker["redis"] = r - if worker['type'] == "sensor": - pw = PiSensorWorker(worker, main_thread_running, system_ready) - print('MudPi Sensors...\t\t\t\033[1;32m Initializing\033[0;0m') - elif worker['type'] == "control": - pw = PiControlWorker(worker, main_thread_running, system_ready) - print('MudPi Controls...\t\t\t\033[1;32m Initializing\033[0;0m') - elif worker['type'] == "i2c": - pw = PiI2CWorker(worker, main_thread_running, system_ready) - print('MudPi I2C...\t\t\t\t\033[1;32m Initializing\033[0;0m') - elif worker['type'] == "lcd": - for lcd in worker['lcds']: - lcd["redis"] = r - pw = LcdWorker(lcd, main_thread_running, system_ready, lcd_available) - lcd_available.set() - print('MudPi LCD Displays...\t\t\t\033[1;32m Initializing\033[0;0m') - elif worker['type'] == "relay": - # Add Relay Worker Here for Better Config Control - print('MudPi Relay...\t\t\t\033[1;32m Initializing\033[0;0m') - else: - raise Exception("Unknown Worker Type: " + worker['type']) - workers.append(pw) + if len(CONFIGS["workers"]) > 0: + for worker in CONFIGS['workers']: + # Create worker for worker + worker["redis"] = r + if worker['type'] == "sensor": + pw = PiSensorWorker(worker, main_thread_running, system_ready) + print('MudPi Sensors...\t\t\t\033[1;32m Initializing\033[0;0m') + elif worker['type'] == "control": + pw = PiControlWorker(worker, main_thread_running, system_ready) + print('MudPi Controls...\t\t\t\033[1;32m Initializing\033[0;0m') + elif worker['type'] == "i2c": + pw = PiI2CWorker(worker, main_thread_running, system_ready) + print('MudPi I2C...\t\t\t\t\033[1;32m Initializing\033[0;0m') + elif worker['type'] == "lcd": + for lcd in worker['lcds']: + lcd["redis"] = r + pw = LcdWorker(lcd, main_thread_running, system_ready, lcd_available) + lcd_available.set() + print('MudPi LCD Displays...\t\t\t\033[1;32m Initializing\033[0;0m') + elif worker['type'] == "relay": + # Add Relay Worker Here for Better Config Control + print('MudPi Relay...\t\t\t\033[1;32m Initializing\033[0;0m') + else: + raise Exception("Unknown Worker Type: " + worker['type']) + workers.append(pw) except KeyError as e: print('MudPi Pi Workers...\t\t\t\033[1;31m Disabled\033[0;0m') print(e) # Worker for relays attached to pi try: - for relay in CONFIGS['relays']: - relay["redis"] = r - relayState = { - "available": threading.Event(), # Event to allow relay to activate - "active": threading.Event() # Event to signal relay to open/close - } - relayEvents[relay.get("key", relay_index)] = relayState - rw = RelayWorker(relay, main_thread_running, system_ready, relayState['available'], relayState['active']) - workers.append(rw) - # Make the relays available, this event is toggled off elsewhere if we need to disable relays - relayState['available'].set() - relay_index +=1 + if len(CONFIGS["relays"]) > 0: + for relay in CONFIGS['relays']: + relay["redis"] = r + relayState = { + "available": threading.Event(), # Event to allow relay to activate + "active": threading.Event() # Event to signal relay to open/close + } + relayEvents[relay.get("key", relay_index)] = relayState + rw = RelayWorker(relay, main_thread_running, system_ready, relayState['available'], relayState['active']) + workers.append(rw) + # Make the relays available, this event is toggled off elsewhere if we need to disable relays + relayState['available'].set() + relay_index +=1 except KeyError: print('MudPi Relays Workers...\t\t\033[1;31m Disabled\033[0;0m') # Load in Actions try: - for action in CONFIGS["actions"]: - print('MudPi Actions...\t\t\t\033[1;32m Initializing\033[0;0m') - action["redis"] = r - a = Action(action) - a.init_action() - actions[a.key] = a + if len(CONFIGS["actions"]) > 0: + for action in CONFIGS["actions"]: + print('MudPi Actions...\t\t\t\033[1;32m Initializing\033[0;0m') + action["redis"] = r + a = Action(action) + a.init_action() + actions[a.key] = a except KeyError: print('MudPi Actions...\t\t\t\033[1;31m Disabled\033[0;0m') # Worker for Triggers - try: - CONFIGS["triggers"]["redis"] = r - t = TriggerWorker(CONFIGS['triggers'], main_thread_running, system_ready, actions) - print('MudPi Triggers...\t\t\t\033[1;32m Initializing\033[0;0m') - workers.append(t) + try: + if len(CONFIGS["triggers"]) > 0: + CONFIGS["triggers"]["redis"] = r + t = TriggerWorker(CONFIGS['triggers'], main_thread_running, system_ready, actions) + print('MudPi Triggers...\t\t\t\033[1;32m Initializing\033[0;0m') + workers.append(t) except KeyError: print('MudPi Triggers...\t\t\t\033[1;31m Disabled\033[0;0m') # Worker for nodes attached to pi via serial or wifi[esp8266, esp32] # Supported nodes: arduinos, esp8266, ADC-MCP3xxx, probably others (esp32 with custom nanpy fork) try: - for node in CONFIGS['nodes']: - node["redis"] = r - if node['type'] == "arduino": - if NANPY_ENABLED: - print('MudPi Arduino Workers...\t\t\033[1;32m Initializing\033[0;0m') - t = ArduinoWorker(node, main_thread_running, system_ready) - else: - print('Error Loading Nanpy library. Did you pip3 install -r requirements.txt?') - elif node['type'] == "ADC-MCP3008": - if MCP_ENABLED: - print('MudPi ADC Workers...\t\t\033[1;32m Initializing\033[0;0m') - t = ADCMCP3008Worker(node, main_thread_running, system_ready) + if len(CONFIGS["nodes"]) > 0: + for node in CONFIGS['nodes']: + node["redis"] = r + if node['type'] == "arduino": + if NANPY_ENABLED: + print('MudPi Arduino Workers...\t\t\033[1;32m Initializing\033[0;0m') + t = ArduinoWorker(node, main_thread_running, system_ready) + else: + print('Error Loading Nanpy library. Did you pip3 install -r requirements.txt?') + elif node['type'] == "ADC-MCP3008": + if MCP_ENABLED: + print('MudPi ADC Workers...\t\t\033[1;32m Initializing\033[0;0m') + t = ADCMCP3008Worker(node, main_thread_running, system_ready) + else: + print('Error Loading MCP3xxx library. Did you pip3 install -r requirements.txt;?') else: - print('Error Loading MCP3xxx library. Did you pip3 install -r requirements.txt;?') - else: - raise Exception("Unknown Node Type: " + node['type']) - nodes.append(t) + raise Exception("Unknown Node Type: " + node['type']) + nodes.append(t) except KeyError as e: print('MudPi Node Workers...\t\t\t\033[1;31m Disabled\033[0;0m')