class Connection(object): def __init__(self): logging.basicConfig(filename="/home/pi/ZeMoCode/Data/ZeMo.log", level=logging.INFO) self.screen = Screen() self.eth0 = self.get_ip("eth0", 3) self.wlan0 = self.get_ip("wlan0", 3) registered = False try: with open("/home/pi/ZeMoCode/account") as f: acnt = f.read() self.account = acnt.strip('\n') self.logInfo("ACCOUNT " + self.account) except: # Occurs if the account file did not create properly from SetupZeMo.sh file self.account = "NULL_ACCOUNT_NAME" if self.account is "NULL_ACCOUNT_NAME": self.screen.drawMessage("No Account Found, Please Retry") self.logInfo("No account found") time.sleep(30) self.piName = socket.gethostname() try: # Tries to load an existing account self.accountJSON = json.load( open('/home/pi/ZeMoCode/account.json')) self.secret = self.accountJSON["secret"] try: self.jsonConfig = self.getConfigData() except: self.logInfo("Did not grab config file") if self.secret is "none": # Loops until registration is completed while registered is False: registered = self.register() except Exception as e: # Registers if no existing account is found self.secret = "none" # Loops until registration is completed while registered is False: registered = self.register() self.screen.drawImage("logo.png", self.screen.background.get_rect(), 223, 57) # Resets account info def resetAccount(self): try: os.remove("/home/pi/ZeMoCode/account.json") except Exception as e: self.logError(e) self.account = "" self.secret = "" self.piName = "" def getaccountJSON(self): return self.accountJSON def getJSONconfig(self): return self.jsonConfig def getAccount(self): return self.account def getPiName(self): return self.piName def getSecret(self): return self.secret def getEth0(self): return self.eth0 def getWlan0(self): return self.wlan0 # Emails ip address def sendIP(self): message = "zfish1 is up and running at (wired, wireless): " + self.getWlan0( ) self.sendEmail(message) # Emails the sensors that are not working def warning(self, sensors): message = "These sensors are not working: " + " ".join( sensors.getName()) self.sendEmail(message) # Emails Log Data from pi def sendLogData(self, files): try: s1 = "" s3 = "" s4 = "" s2 = "" with open(files[0]) as f: s1 = f.read() + '\n' with open(files[1]) as f: s2 = f.read() + '\n' with open(files[2]) as f: s3 = f.read() + '\n' with open(files[3]) as f: s4 = f.read() + '\n' values = { "body": "Log Data", "attachments": [{ "filename": files[0], "content": s1 }, { "filename": files[1], "content": s2 }, { "filename": files[2], "content": s3 }, { "filename": files[3], "content": s4 }] } url = 'https://zemoproject.org/notifications/' + self.account + '/' + self.piName params = json.dumps(values) bearer = 'Bearer ' + self.secret headers = { 'content-type': 'application/json', 'Authorization': bearer } req = requests.post(url=url, json=values, headers=headers) self.screen.drawMessage("Log Data Sent!") time.sleep(2) except Exception as e: self.logError(e) self.screen.drawMessage("Did not send email") time.sleep(2) # Returns the user settings from the website def getConfigData(self): try: url = 'https://zemoproject.org/settings/' + self.account + '/' + self.piName bearer = 'Bearer ' + self.secret headers = { 'content-type': 'application/json', 'Authorization': bearer } req = requests.get(url=url, headers=headers) return req.json() except Exception as e: self.logInfo("In getConfigData") self.logInfo("account: " + str(self.account)) self.logInfo("piName: " + str(self.piName)) self.logInfo("ip: " + str(self.getWlan0())) self.logError(e) # Sends the hourly readings def sendReadings(self, values): try: params = json.dumps(values).encode('utf8') emailEndPoint = "https://zemoproject.org/data/" + self.account + "/" + self.piName bearer = 'Bearer ' + self.secret req = urllib.request.Request(emailEndPoint, data=params, headers={ 'content-type': 'application/json', 'Authorization': bearer }) response = urllib.request.urlopen(req) except Exception as e: self.logError(e) def sendEmail(self, message): try: values = {'body': message} params = json.dumps(values).encode('utf8') emailEndPoint = 'https://zemoproject.org/notifications/' + self.account + '/' + self.piName bearer = 'Bearer ' + self.secret req = urllib.request.Request(emailEndPoint, data=params, headers={ 'content-type': 'application/json', 'Authorization': bearer }) response = urllib.request.urlopen(req) except Exception as e: self.logError(e) # Sends an email of the probes out of range with the associated data def sendOutofRange(self, sensors): try: compiledMsg = "These sensors are out of range:" message = "" for probe in sensors: message = message + "\n" + probe.getName( ) + ": " + probe.getData() compiledMsg = compiledMsg + message + " \nThe data here includes the trimmed readings." values = {'body': compiledMsg} params = json.dumps(values).encode('utf8') emailEndPoint = 'https://zemoproject.org/notifications/' + self.account + '/' + self.piName bearer = 'Bearer ' + self.secret req = urllib.request.Request(emailEndPoint, data=params, headers={ 'content-type': 'application/json', 'Authorization': bearer }) response = urllib.request.urlopen(req) except Exception as e: self.logError(e) # Sends registration info, pi should appear on website afterwards def sendRegister(self): try: values = { "account": self.account, "name": self.piName, "ip_address": self.getWlan0() } url = 'https://zemoproject.org/register' params = json.dumps(values) headers = {'content-type': 'application/json'} req = requests.post(url=url, json=values, headers=headers) sec = req.json() self.secret = sec["secret"] return True except: self.logInfo(str(values)) self.logInfo("rec: " + str(req)) return False # Draws the registration screen and responds to user input def screenRegisterResend(self): resend = self.screen.register_screen() if resend is False: self.screen.drawMessage("Attempting to Register") elif resend is True: self.sendRegister() self.screen.drawMessage("New Request Sent") time.sleep(2) # Waits for user to push accept on the website def waitForAccept(self): try: self.jsonConfig = self.getConfigData() if self.jsonConfig is None or self.secret is "none": self.screenRegisterResend() return False return True except: self.screenRegisterResend() return False # Registers pi def register(self): try: self.sendRegister() accepted = False while accepted is False: accepted = self.waitForAccept() cfg = { "secret": self.secret, "account": self.account, "piName": self.piName } with open("/home/pi/ZeMoCode/account.json", "w") as g: json.dump(cfg, g) self.accountJSON = json.load( open('/home/pi/ZeMoCode/account.json')) self.screen.canvas.fill((0, 0, 0)) self.screen.drawImage("logo.png", self.screen.background.get_rect(), 223, 57) return True except Exception as e: self.logInfo("In Register Function") self.logInfo("account: " + str(self.account)) self.logInfo("piName: " + str(self.piName)) self.logInfo("ip: " + str(self.getWlan0())) self.logError(e) self.screen.drawMessage("Failed to grab Settings") time.sleep(2) self.screen.drawMessage("Make sure to \"Accept\"") time.sleep(2) return False # Returns IP address of pi def get_ip(self, network, countdown): try: countdown = countdown - 1 if (countdown is not 0): s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) return socket.inet_ntoa( fcntl.ioctl( s.fileno(), 0x8915, struct.pack('256s', bytes(network[:15], 'utf-8')))[20:24]) except: self.get_ip(network, countdown) # Logging errors def logError(self, info): try: logging.exception(str(info)) except: pass # Logging Info def logInfo(self, info): try: logging.info(str(info)) except: pass
class App(object): def __init__(self): print("ZeMo is Running") self.screen = Screen() self.screen.drawImage("logo.png", self.screen.background.get_rect(), 223, 57) self.conn = Connection() self.done = False self.takeReadFlag = True self.readingNow = False self.waitTime = 0 jsonFile = self.conn.getJSONconfig() self.daysToKeep = jsonFile["settings"]["days"] self.readsPerDay = jsonFile["settings"]["reads"] self.timeList = [] piName = self.conn.getPiName() self.phSensor = PH(jsonFile, piName, self.screen) self.condSensor = Conductivity(jsonFile, piName, self.screen) self.dOSensor = DissolvedOxygen(jsonFile, piName, self.screen) self.tempSensor = Temperature(jsonFile, piName, self.screen) self.sensorList = [] self.sensorList.append(self.tempSensor) self.sensorList.append(self.condSensor) self.sensorList.append(self.phSensor) self.sensorList.append(self.dOSensor) for sensor in self.sensorList: sensor.takeRead(self.conn) self.t2 = Thread(target=App.checkTime_loop, args=(self,)) self.t2.start() self.update_reads_per_day() def checkQuit(self, eventType): try: if eventType == pg.QUIT or pg.key.get_pressed()[pg.K_ESCAPE]: self.done = True self.screen.quit() sys.exit() except Exception as e: self.conn.logError(e) # Checks the values and emails if values are out of range, the ooR occurs here def takeReads_checkAlarms(self): try: sensors = [] jsonFile = self.conn.getConfigData() self.daysToKeep = jsonFile["settings"]["days"] self.readsPerDay = jsonFile["settings"]["reads"] self.update_reads_per_day() piName = self.conn.getPiName() for sensor in self.sensorList: sensor.refresh(jsonFile, piName) sensor.takeRead(self.conn) read = float(sensor.getCurrRead()) if read > float(sensor.getHighRange()) or read < float(sensor.getLowRange()): sensors.append(sensor) if len(sensors) > 0: self.conn.sendOutofRange(sensors) except Exception as e: self.conn.logError(e) # Updates the list of times checked in the CheckTime() function def update_reads_per_day(self): try: if int(self.readsPerDay) > 96: self.readsPerDay = "96" self.timeList = [] hours = 24 / int(self.readsPerDay) i = 0.00 j = 0.00 while i < 24: i = hours + i if i < 24: addHour = int(i) y = i % 1 j = (y * 60) / 100 addTime = addHour + round(j, 2) addThis = str(round(addTime, 2)) self.timeList.append(addThis) except Exception as e: self.conn.logError(e) # A constantly running loop that has an individual thread # Checks the time for taking automated reads def checkTime_loop(self): while not self.done: try: currMin = datetime.now().minute currHour = datetime.now().hour if int(currHour) < 1: currHour = "0" else: currHour = str(round(int(currHour), 0)) if currMin > 9: uCurrTime = currHour + "." + str(round(currMin, 0)) elif currMin < 1: uCurrTime = currHour + ".0" else: uCurrTime = currHour + ".0" + str(round(currMin, 0)) currTime = str(uCurrTime) #potential increase to speed, try: #self.timeList.index(currTime) if(currTime in self.timeList and self.takeReadFlag is True) or currTime == "0.0": self.takeReadFlag = False self.waitTime = currMin + 1 if(self.waitTime > 59): self.waitTime = 0 self.readingNow = True try: takingReads = Thread(target=App.taking_reads_loop, args=(self,)) takingReads.start() except Exception as e: self.conn.logError(e) self.screen.drawMessage("Taking Reads") self.takeReads_checkAlarms() pg.event.clear() self.readingNow = False time.sleep(2) elif self.waitTime < int(currMin) and self.waitTime != 0: self.takeReadFlag = True except Exception as e: self.conn.logError(e) self.readingNow = False # Advanced Settings def advanced_settings_event(self): try: while not self.done: if not self.readingNow: self.screen.advanced_settings_event_screen() pg.display.update() pg.event.clear() pg.event.wait() if not self.readingNow: for event in pg.event.get(): self.checkQuit(event.type) if event.type == pg.MOUSEBUTTONDOWN: button = self.screen.checkCollision(event.pos) if button is 5: return elif button is 1: files = [] for item in self.sensorList: file2 = item.getFilename()[:-4] files.append(file2 + "_log.csv") self.conn.sendLogData(files) self.screen.drawMessage("Log Files Emailed") elif button is 2: for item in self.sensorList: item.deleteHistory() self.screen.drawMessage("Data removed") time.sleep(2) self.screen.drawMessage("Next sync will update graphs") time.sleep(2) elif button is 3: #re-register - clears all the input data of piName, secret key, and account if self.screen.reregister() is True: self.conn.resetAccount() jsonFile = self.conn.getConfigData() piName = self.conn.getPiName() for sensor in self.sensorList: sensor.refresh(jsonFile, piName) self.screen.drawMessage("Re-download the software") time.sleep(4) self.done = True else: self.screen.advanced_settings_event_screen() pg.display.update() elif button is 4: self.done = True return except Exception as e: self.conn.logError(e) def settings_event(self): try: while not self.done: if not self.readingNow: self.screen.settings_event_screen(self.conn.getEth0(), self.conn.getWlan0(), self.readsPerDay, self.daysToKeep) pg.display.update() pg.event.clear() pg.event.wait() if not self.readingNow: for event in pg.event.get(): self.checkQuit(event.type) if event.type == pg.MOUSEBUTTONDOWN: button = self.screen.checkCollision(event.pos) if button is 5: return elif button is 7: self.advanced_settings_event() elif button is 2: #refresh all sensors jsonFile = self.conn.getConfigData() piName = self.conn.getPiName() for sensor in self.sensorList: sensor.refresh(jsonFile, piName) except Exception as e: self.conn.logError(e) # Screen shows "Taking Reads..." def taking_reads_loop(self): try: while(self.readingNow): self.screen.drawMessage("Taking Reads ") pg.display.update() if(self.readingNow): time.sleep(1) self.screen.drawMessage("Taking Reads. ") pg.display.update() if(self.readingNow): time.sleep(1) self.screen.drawMessage("Taking Reads.. ") pg.display.update() if(self.readingNow): time.sleep(1) self.screen.drawMessage("Taking Reads...") pg.display.update() if(self.readingNow): time.sleep(1) self.screen.canvas.fill((0,0,0)) self.screen.canvas.fill((0,0,0)) except Exception as e: self.conn.logError(e) # Update Probe def update_event(self, sensor): while not self.done: if not self.readingNow: try: self.screen.update_event_screen(sensor) pg.display.update() pg.event.clear() pg.event.wait() if not self.readingNow: for event in pg.event.get(): self.checkQuit(event.type) if event.type == pg.MOUSEBUTTONDOWN: button = self.screen.checkCollision(event.pos) if button is 1: sensor.calibrate() elif button is 2: jsonFile = self.conn.getConfigData() piName = self.conn.getPiName() sensor.refresh(jsonFile, piName) elif button is 5: return elif button is 8 or button is 4 or button is 3: try: self.readingNow = True takingReads = Thread(target=App.taking_reads_loop, args=(self,)) takingReads.start() jsonFile = self.conn.getConfigData() piName = self.conn.getPiName() sensor.refresh(jsonFile, piName) sensor.takeRead(self.conn) self.readingNow = False except: self.readingNow = False time.sleep(1) self.screen.update_event_screen(sensor) except Exception as e: self.conn.logError(e) # Switches between the event loops depending on button pressed def main_event_loop(self): while not self.done: try: if not self.readingNow: self.screen.main_menu_screen(self.condSensor.getCurrRead(), self.dOSensor.getCurrRead(), self.phSensor.getCurrRead(), self.tempSensor.getCurrRead()) pg.display.update() pg.event.clear() pg.event.wait() if not self.readingNow: for event in pg.event.get(): self.checkQuit(event.type) if event.type == pg.MOUSEBUTTONDOWN: button = self.screen.checkCollision(event.pos) if button is 7: self.settings_event() elif button is 1: self.update_event(self.condSensor) elif button is 2: self.update_event(self.dOSensor) elif button is 3: self.update_event(self.phSensor) elif button is 4: self.update_event(self.tempSensor) except Exception as e: self.conn.logError(e)