From 36850c608b00a20efa39c08ffc3cfa9b745efbac Mon Sep 17 00:00:00 2001 From: hydrosys4 Date: Sat, 4 Sep 2021 10:11:22 +0200 Subject: [PATCH] Added new WateringPlan function --- ActuatorControllermod.py | 82 +++++++ GPIOEXPI2Ccontrol.py | 61 +++-- HC12control.py | 373 +++++++++++++++++++++++++----- HC12mod.py | 223 +++++++++++++++--- HWcontrol.py | 236 ++++++------------- MQTTcontrol.py | 143 ++++-------- advancedmod.py | 47 +++- autofertilizermod.py | 2 +- automationmod.py | 64 +---- autowateringmod.py | 6 +- bentornado.py | 8 +- changelog/change | 41 ++++ filestoragemod.py | 5 +- hardwaremod.py | 31 ++- interruptmod.py | 64 +---- networkdbmod.py | 13 +- networkmod.py | 17 +- selectedplanmod.py | 78 ++++--- start.py | 54 +++-- templates/advanced.html | 132 ++++++++++- templates/layout.html | 2 +- templates/messagebox.html | 2 +- templates/networksetting.html | 19 ++ templates/showmqttdevicelist.html | 3 +- templates/wateringplan.html | 2 +- 25 files changed, 1115 insertions(+), 593 deletions(-) create mode 100644 ActuatorControllermod.py mode change 100644 => 100755 bentornado.py diff --git a/ActuatorControllermod.py b/ActuatorControllermod.py new file mode 100644 index 0000000..f0836bd --- /dev/null +++ b/ActuatorControllermod.py @@ -0,0 +1,82 @@ +import logging +import hardwaremod +import emailmod +import actuatordbmod +import autofertilizermod + + +logger = logging.getLogger("hydrosys4."+__name__) + +def activateactuator(target, value): # return true in case the state change: activation is >0 or a different position from prevoius position. + # check the actuator + isok=False + out="" + actuatortype=hardwaremod.searchdata(hardwaremod.HW_INFO_NAME,target,hardwaremod.HW_CTRL_CMD) + actuatortypelist=actuatortype.split("/") + if actuatortypelist: + actuatortype=actuatortypelist[0] + print (" Actuator " + actuatortype + " target " + target) + supportedactuators=["pulse","servo","stepper"] + # stepper motor + if actuatortype=="stepper": + out, isok = hardwaremod.GO_stepper_position(target,value) + if isok: + actuatordbmod.insertdataintable(target,value) + + # hbridge motor + if actuatortype=="hbridge": + out, isok = hardwaremod.GO_hbridge_position(target,value) + if isok: + actuatordbmod.insertdataintable(target,value) + + # pulse + if actuatortype=="pulse": + duration=hardwaremod.toint(value,0) + # check the fertilizer doser flag before activating the pulse + doseron=autofertilizermod.checkactivate(target,duration) + # start pulse + out, isok=hardwaremod.makepulse(target,duration) + # salva su database + if isok: + actuatordbmod.insertdataintable(target,duration) + + # servo motor + if actuatortype=="servo": + out, isok = hardwaremod.servoangle(target,value,0.5) + if isok: + actuatordbmod.insertdataintable(target,value) + + # photo + if actuatortype=="photo": + duration=hardwaremod.toint(value,0) + if duration>0: + isok=hardwaremod.takephoto(True) + # save action in database + if isok: + actuatordbmod.insertdataintable(target,1) + + # mail + if (actuatortype=="mail+info+link")or(actuatortype=="mail+info"): + if value>0: + mailtext=str(value) + isok=emailmod.sendmail(target,"info","Automation Value:" + mailtext) + # save action in database + if isok: + actuatordbmod.insertdataintable(target,1) + + + return out , isok + + + + +if __name__ == '__main__': + + """ + prova functions + """ + target="Relay1_1" + value="10" + + + diff --git a/GPIOEXPI2Ccontrol.py b/GPIOEXPI2Ccontrol.py index 73413fb..d754764 100644 --- a/GPIOEXPI2Ccontrol.py +++ b/GPIOEXPI2Ccontrol.py @@ -107,6 +107,15 @@ def tonumber(thestring, outwhenfail): except: return outwhenfail +def returnmsg(recdata,cmd,msg,successful): + recdata.clear() + print(msg) + recdata.append(cmd) + recdata.append(msg) + recdata.append(successful) + if not successful: + logger.error("Error: %s" ,msg) + return True def execute_task(cmd, message, recdata): global hbridge_data @@ -131,12 +140,10 @@ def execute_task(cmd, message, recdata): else: - print("Command not found") - recdata.append(cmd) - recdata.append("e") - recdata.append(0) - return False; - return False; + msg="Command not found" + returnmsg(recdata,cmd,msg,0) + return False + return False def execute_task_fake(cmd, message, recdata): @@ -149,11 +156,9 @@ def execute_task_fake(cmd, message, recdata): else: - print("no fake command available" , cmd) - recdata.append(cmd) - recdata.append("e") - recdata.append(0) - return False; + msg="no fake command available" + cmd + returnmsg(recdata,cmd,msg,0) + return False return True @@ -326,9 +331,7 @@ def gpio_pulse(cmd, message, recdata): print("No Action, pulse activated when PIN already active and activationmode is NOADD") logger.warning("No Action, pulse activated when PIN already active and activationmode is NOADD") successflag=1 - recdata.append(cmd) - recdata.append(PIN) - recdata.append(successflag) + returnmsg(recdata,cmd,PIN,successflag) return True @@ -349,11 +352,7 @@ def gpio_pulse(cmd, message, recdata): pulseok=GPIO_output(address, PIN, level) if not pulseok: msg="Not able to activate the pulse in GPIO Expansion, Address I2C: " + address + " PIN: "+ PIN - print(msg) - logger.error(msg) - recdata.append(cmd) - recdata.append(msg) - recdata.append(0) + returnmsg(recdata,cmd,msg,0) return True @@ -364,9 +363,7 @@ def gpio_pulse(cmd, message, recdata): #print "pulse started", time.ctime() , " PIN=", PIN , " Logic=", logic successflag=1 - recdata.append(cmd) - recdata.append(PIN) - recdata.append(successflag) + returnmsg(recdata,cmd,PIN,successflag) return True def gpio_stoppulse(cmd, message, recdata): @@ -394,9 +391,7 @@ def gpio_stoppulse(cmd, message, recdata): print("No Action, Already OFF") logger.warning("No Action, Already OFF") successflag=1 - recdata.append(cmd) - recdata.append(PIN) - recdata.append(successflag) + returnmsg(recdata,cmd,PIN,successflag) return True @@ -406,8 +401,8 @@ def gpio_stoppulse(cmd, message, recdata): PINthreadID.cancel() endpulse(address, PIN,logic,POWERPIN) #this also put powerpin off - recdata.append(cmd) - recdata.append(PIN) + + returnmsg(recdata,cmd,PIN,1) return True @@ -418,13 +413,12 @@ def gpio_pin_level(cmd, message, recdata): if address=="": address="0x20" - recdata.append(msgarray[0]) PINlevel=statusdataDBmod.read_status_data(GPIO_data,address+PIN,"level") if PINlevel is not None: - recdata.append(str(PINlevel)) + returnmsg(recdata,cmd,str(PINlevel),1) return True else: - recdata.append("e") + returnmsg(recdata,cmd,"error",0) return False @@ -437,18 +431,15 @@ def read_input_pin(cmd, message, recdata): #print " read pin input ", message PINstr=msgarray[1] isRealPIN,PIN=CheckRealHWpin(PINstr) - recdata.append(cmd) if isRealPIN: if GPIO.input(PIN): reading="1" else: reading="0" - recdata.append(reading) - recdata.append(successflag) + returnmsg(recdata,cmd,reading,successflag) else: successflag=0 - recdata.append("e") - recdata.append(successflag) + returnmsg(recdata,cmd,"error",successflag) return True def isPinActive(address, PIN, logic): diff --git a/HC12control.py b/HC12control.py index d9afb62..a4d398b 100644 --- a/HC12control.py +++ b/HC12control.py @@ -13,6 +13,11 @@ ISRPI=False HWCONTROLLIST=[] pinlist=["14","15","4"] +# IMPORTANT ***************************************************************************** +# the following pins should not be set as output otherwise they lose the Serial configuration +# PIN=14 TX PIN=15 RX + + def initHC12(): HC12mod.initHC12() @@ -20,20 +25,20 @@ def initHC12(): global HWCONTROLLIST ISRPI=HC12mod.HC12radionet.mediumconnected if ISRPI: - HWCONTROLLIST=["readinput/HC12"] + HWCONTROLLIST=["readinput/HC12","pulse/HC12","stoppulse/HC12","pinstate/HC12"] else: HWCONTROLLIST=[] +# status variables +GPIO_data={} +GPIO_data["default"]={"level":0, "state":None, "threadID":None} +PowerPIN_Status={} +PowerPIN_Status["default"]={"level":0, "state":"off", "pinstate":None, "timeZero":0} -# IMPORTANT ***************************************************************************** -# the following pins should not be set as output otherwise they lose the Serial configuration -# PIN=14 TX PIN=15 RX - -# status variables def toint(thestring, outwhenfail): try: @@ -50,6 +55,15 @@ def tonumber(thestring, outwhenfail): except: return outwhenfail +def returnmsg(recdata,cmd,msg,successful): + recdata.clear() + print(msg) + recdata.append(cmd) + recdata.append(msg) + recdata.append(successful) + if not successful: + logger.error("Error: %s" ,msg) + return True def execute_task(cmd, message, recdata): @@ -60,22 +74,298 @@ def execute_task(cmd, message, recdata): if cmd==HWCONTROLLIST[0]: # readinput - return readinput_HC12(cmd, message, recdata) + return HC12_readinput(cmd, message, recdata) + + if cmd==HWCONTROLLIST[1]: # pulse + return HC12_pulse(cmd, message, recdata) + elif cmd==HWCONTROLLIST[2]: # stoppulse + return HC12_stoppulse(cmd, message, recdata) + + elif cmd==HWCONTROLLIST[3]: # pinstate + return HC12_pin_level(cmd, message, recdata) + + else: + returnmsg(recdata,cmd,"Command not found",0) + return False + return False + + + + + + +def powerPIN_start(REGID,CMD,address,pulsesecond,ignorepowerpincount=False): # powerpin will work only with same address and same topic/"powerpin number" + if REGID!="": + PowerPINlevel=statusdataDBmod.read_status_data(PowerPIN_Status,REGID,"level") + #start power pin + if PowerPINlevel<1: + PowerPINlevel==0 + if not ignorepowerpincount: + statusdataDBmod.write_status_data(PowerPIN_Status,REGID,"level",PowerPINlevel+1) + + + # complication below is necessary. + timeZero=int(time.time())+pulsesecond + Lasttimezero=statusdataDBmod.read_status_data(PowerPIN_Status,REGID,"timeZero") + if Lasttimezero: + if timeZero>Lasttimezero: + waittime=1 # seconds + statusdataDBmod.write_status_data(PowerPIN_Status,REGID,"timeZero",timeZero) + HC12_output(CMD, address,1) + time.sleep(waittime) + else: + statusdataDBmod.write_status_data(PowerPIN_Status,REGID,"timeZero",timeZero) + + + if PowerPINlevel==0: + time.sleep(0.2) + return True + + +def powerPIN_stop(CMD_PWR,waittime,address): + REGID=CMD_PWR["ID"] + if REGID!="": + PowerPINlevel=statusdataDBmod.read_status_data(PowerPIN_Status,REGID,"level") + statusdataDBmod.write_status_data(PowerPIN_Status,REGID,"level",PowerPINlevel-1) + + #stop power pin if level less or equal to zero + if (PowerPINlevel-1)<=0: + + time.sleep(waittime) + + HC12_output(CMD_PWR["STOP"], address,3) + + + return True + + + +def HC12_output(CMD_LIST,address, retry): + + for CMD in CMD_LIST: # in case of HC-12 list is made always by one command, otherwise we have timing problems + topic=CMD["topic"] + value=CMD["value"] + print("HC12-CMD string: " + " " + topic + " " + value) + # topic is the concatemation of 3 strings: title+//+PIN + # where can be /StartPulse/ or /StopPulse/ + # value is the duration in seconds + # Key is the title which is the same as the chiperkey + isok, msg = HC12mod.HC12radionet.sendCommand(topic, value, retry) + #clientobj.publish(topic=topic, payload=str(payload), qos=2) + time.sleep(0.1) + + return isok, msg + + +def endpulse(PIN_CMD,POWERPIN_CMD,address): + REGID=PIN_CMD["ID"] + statusdataDBmod.write_status_data(GPIO_data,REGID,"threadID",None) + + HC12_output(PIN_CMD["STOP"], address, 1) + + endwaittime=0 + powerPIN_stop(POWERPIN_CMD,endwaittime,address) + + #print "pulse ended", time.ctime() , " PIN=", PINstr , " Logic=", logic , " Level=", level + return True + + +def create_pulse_CMD_list(PIN,POWERPIN,title,pulsesecond): + # This function created the right format for the command to send to the HC12 + # The CMD dictionary will have one ID indicating the Title and PIN, and 2 commands: start, stop + + Durationperiod=pulsesecond + + PINnum=toint(PIN,0) + PINstr="" + if not PINnum==0: + PINstr=PIN + # MQTT publish commands + MQTT_CMD={"ID":title+PINstr} + CMD={"topic":title+"/StartPulse/"+PINstr,"value":str(Durationperiod)} + CMD_list=[] + CMD_list.append(CMD) + MQTT_CMD["START"]=CMD_list + + CMD={"topic":title+"/StopPulse/"+PINstr,"value":"0"} + CMD_list=[] + CMD_list.append(CMD) + MQTT_CMD["STOP"]=CMD_list + + + MQTT_CMD_PWR={"ID":"","START":"","STOP":""} + POWERPINstr="" + POWERPINnum=toint(POWERPIN,0) + if not POWERPINnum==0: # commands are filled only if the PWRPIN is a number + POWERPINstr=POWERPIN + + waittime=0.2 + + # MQTT publish commands PWR + ID=title+POWERPINstr + MQTT_CMD_PWR={"ID":ID} + CMD={"topic":title+"/StartPulse/"+POWERPINstr,"value":str(Durationperiod+waittime*2)} + CMD_list=[] + CMD_list.append(CMD) + MQTT_CMD_PWR["START"]=CMD_list + + CMD={"topic":title+"/StopPulse/"+POWERPINstr,"value":"0"} + CMD_list=[] + CMD_list.append(CMD) + MQTT_CMD_PWR["STOP"]=CMD_list + + return MQTT_CMD, MQTT_CMD_PWR + + +def HC12_pulse(cmd, message, recdata): + successflag=0 + msgarray=message.split(":") + messagelen=len(msgarray) + PIN=msgarray[1] + + testpulsetime=msgarray[2] # in seconds + pulsesecond=int(testpulsetime) + + if pulsesecond<10: + msg="HC-12 cannot handle pulses with duration less than 10sec" + successflag=0 + returnmsg(recdata,cmd,msg,successflag) + return True + + POWERPIN="" + if messagelen>4: + POWERPIN=msgarray[4] + + + activationmode="" + if messagelen>7: + activationmode=msgarray[7] + + address="" # this is the MQTT client ID + if messagelen>8: + address=msgarray[8] + + title="" + if messagelen>9: + title=msgarray[9] + + + if title=="": + msg = "No topic specified, please insert it in the Title field" + successflag=0 + returnmsg(recdata,cmd,msg,successflag) + return True + + + MQTT_CMD, MQTT_CMD_PWR = create_pulse_CMD_list(PIN,POWERPIN,title,pulsesecond) + + # start pulse activation logic + + REGID=MQTT_CMD["ID"] # this is made by the string "topic"+"PIN" + ignorepowerpincount=False + # in case another timer is active on this TOPIC ID it means that the PIN is activated + PINthreadID=statusdataDBmod.read_status_data(GPIO_data,REGID,"threadID") + if not PINthreadID==None: + + # pin already active + if activationmode=="NOADD": # no action needed + successflag=1 + returnmsg(recdata,cmd,PIN,successflag) + return True + + PINthreadID.cancel() # cancel thread + + ignorepowerpincount=True # do not add levels to the powerpin + + + + powerPIN_start(MQTT_CMD_PWR["ID"],MQTT_CMD_PWR["START"],address,pulsesecond,ignorepowerpincount) + + level=1 + statusdataDBmod.write_status_data(GPIO_data,REGID,"level",level) + logger.info("Set PIN=%s to State=%s", REGID, str(level)) + print (REGID + " *********************************************** " , level) + + isok , msg = HC12_output(MQTT_CMD["START"],address, 2) # try two times + + NewPINthreadID=threading.Timer(pulsesecond, endpulse, [MQTT_CMD, MQTT_CMD_PWR, address]) + NewPINthreadID.start() + statusdataDBmod.write_status_data(GPIO_data,REGID,"threadID",NewPINthreadID) + + + if isok: + + successflag=1 + returnmsg(recdata,cmd,PIN,successflag) else: - print("Command not found") - recdata.append(cmd) - recdata.append("e") - recdata.append(0) - return False; - return False; + successflag=0 + returnmsg(recdata,cmd,msg,successflag) + + return True +def HC12_stoppulse(cmd, message, recdata): # when ON send MQTT message with the duration in seconds of the activation, and when OFF send zero. + print(" Don't stop me now ") + + msgarray=message.split(":") + messagelen=len(msgarray) + PIN=msgarray[1] + + logic="pos" + if messagelen>3: + logic=msgarray[3] + + POWERPIN="" + if messagelen>4: + POWERPIN=msgarray[4] + + + address="" # this is the MQTT client ID + if messagelen>7: + address=msgarray[7] -def readinput_HC12(cmd, message, recdata): + + title="" + if messagelen>8: + title=msgarray[8] + + if title=="": + msg="No topic specified" + successflag=0 + returnmsg(recdata,cmd,msg,successflag) + return True + + MQTT_CMD, MQTT_CMD_PWR = create_pulse_CMD_list(PIN,POWERPIN,title,0) + + REGID=MQTT_CMD["ID"] + PINthreadID=statusdataDBmod.read_status_data(GPIO_data,REGID,"threadID") + if not PINthreadID==None: + #print "cancel the Thread of PIN=",PIN + PINthreadID.cancel() + + endpulse(MQTT_CMD, MQTT_CMD_PWR,address) #this also put powerpin off + returnmsg(recdata,cmd,PIN,1) + return True + + +def HC12_pin_level(cmd, message, recdata): + msgarray=message.split(":") + PIN=msgarray[1] + PINlevel=statusdataDBmod.read_status_data(GPIO_data,PIN,"level") + if PINlevel is not None: + returnmsg(recdata,cmd,str(PINlevel),1) + return True + else: + msg="Not found" + returnmsg(recdata,cmd,msg,0) + return True + + +def HC12_readinput(cmd, message, recdata): print("HC12 inputs ..............................") @@ -120,12 +410,8 @@ def readinput_HC12(cmd, message, recdata): if (Topic==""): - print("Error, HC12 reading no topic defined", Topic) - # address not correct - logger.error("HC12 reading no topic defined %s", Topic) - recdata.append(cmd) - recdata.append("Topic not configured ") - recdata.append(0) + msg = "Error, HC12 reading no topic defined" + Topic + returnmsg(recdata,cmd,msg,0) return True # Get the last value pubished for this topic. @@ -137,12 +423,8 @@ def readinput_HC12(cmd, message, recdata): timestamp=datadict.get("timestamp") isok=True else: - print("Error, HC12 reading no Reading for the topic ", Topic) - # address not correct - logger.error("HC12 reading no Reading for the topic %s", Topic) - recdata.append(cmd) - recdata.append("Error, HC12 no Reading for the topic " + Topic) - recdata.append(0) + msg= "Error, HC12 reading no Reading for the topic" + Topic + returnmsg(recdata,cmd,msg,0) return True deltaseconds=(datetime.utcnow()-timestamp).total_seconds() @@ -151,12 +433,8 @@ def readinput_HC12(cmd, message, recdata): if deltaseconds<0: # reading happenend in the future .... somethign wrong, issue error - print("Error, reading in the future ") - # address not correct - logger.error("reading in the future") - recdata.append(cmd) - recdata.append("Error, reading in the future ...") - recdata.append(0) + msg="Error, reading in the future " + returnmsg(recdata,cmd,msg,0) return True @@ -172,12 +450,8 @@ def readinput_HC12(cmd, message, recdata): if isok: if result=="": - print("Error, no updates from the sensor since ", str(deltaseconds) , " (sec)") - # address not correct - logger.error(" no updates from the sensor %s (sec)", str(deltaseconds)) - recdata.append(cmd) - recdata.append("Error, no updates from the sensor") - recdata.append(0) + msg = "Error, no updates from the sensor since " + str(deltaseconds) + " (sec)" + returnmsg(recdata,cmd,msg,0) return True else: @@ -185,31 +459,26 @@ def readinput_HC12(cmd, message, recdata): successflag=1 logger.info("HC12 input reading: %s", reading) print("HC12 input reading ", reading) - recdata.append(cmd) - recdata.append(reading) - recdata.append(successflag) + returnmsg(recdata,cmd,reading,successflag) statusmsg="HC12 last update " + '{:.1f}'.format(deltaseconds) + " seconds " recdata.append(statusmsg) return True - print("Error, HC12 reading") - logger.error("HC12 reading") - recdata.append(cmd) - recdata.append("Generic Error, MQTT reading") - recdata.append(0) + msg="Error, HC12 reading" + returnmsg(recdata,cmd,msg,0) return True def sendcommand(cmd, message, recdata): - # as future upgrade this function might be run asincronously using "import threading" + # as future upgrade this function might be run asincronously using "import threading" - if ISRPI: - ack=execute_task(cmd, message, recdata) - else: - print(" Client to support HC12 not installed") - return ack + if ISRPI: + ack=execute_task(cmd, message, recdata) + else: + print(" Client to support HC12 not installed") + return ack if __name__ == '__main__': diff --git a/HC12mod.py b/HC12mod.py index 8179b7a..a4520f2 100644 --- a/HC12mod.py +++ b/HC12mod.py @@ -33,8 +33,9 @@ def setserial(self): ser=None try: ser = serial.Serial(port=self.port,baudrate=self.baudrate,timeout=self.timeout,xonxoff=False) # timeout is in seconds - time.sleep(1) - ser.flushInput() + time.sleep(0.1) + #ser.flushInput() + #time.sleep(0.2) except Exception as e: print (e) logger.warning("Not able to connect to the Serial interface, try to enable it using raspi-config") @@ -94,7 +95,6 @@ def waitfordata(self,count): if not self.serial: return # wait for data - count=4 while (not self.serial.inWaiting())and(count>0): time.sleep(0.1) count=count-1 @@ -135,6 +135,7 @@ def sendString(self,stringdata): def sendBytes(self,bytesdata): if not self.serial: + print("Serial Not Connected ") return self.serial.write(bytesdata) @@ -211,43 +212,59 @@ def setAT(self, datadict): def VerifySerialAT(self): isok=False #send first AT command just to check the HC-12 is answering - print("Check if AT commnds are working") + print("Check if AT commands are working") cmd="AT\n" outok , received_data = self.sendReceiveATcmds(cmd) if outok: if b"ok" in received_data or b"OK" in received_data: isok=True print ("Check AT Successfull") + else: + #self.disableATpin() + #self.enableATpin() + time.sleep(0.5) return isok + def enableATpin(self): + print (" Enable AT pin") + # the HC-12 set pin should be put to LOW to enable AT commands + GPIO.setmode(GPIO.BCM) + GPIO.setup(self.SetPIN, GPIO.OUT) + GPIO.output(self.SetPIN, GPIO.LOW) # set HC12 to AT mode + time.sleep(1) + + def disableATpin(self): + print (" Disable AT pin") + GPIO.output(self.SetPIN, GPIO.HIGH) # set HC12 to normal mode + time.sleep(0.3) + + def VerifySerialATwithPIN(self): # list the requireb baudrate baudRateList=[1200,9600] # pause the receiver based - # the HC-12 set pin should be put to LOW to enable AT commands - GPIO.setmode(GPIO.BCM) - GPIO.setup(self.SetPIN, GPIO.OUT, initial=GPIO.LOW) # set pin 7 (GPIO4) to OUTPUT (SET pin of HC12) - GPIO.output(self.SetPIN, GPIO.LOW) # set HC12 to AT mode - time.sleep(0.2) + self.enableATpin() isok=False for baudrate in baudRateList: + self.ser=_SerialConnection(baudrate=baudrate) if self.ser.serialok: self.ser.listenPauseFlag=True - inde=2 + inde=5 isok=False while (inde>0)and(not isok): isok=self.VerifySerialAT() inde=inde-1 if isok: break + else: break - GPIO.output(self.SetPIN, GPIO.HIGH) # set HC12 to normal mode - time.sleep(0.5) + self.disableATpin() + # pause the receiver based self.ser.listenPauseFlag=False return isok @@ -259,6 +276,7 @@ def sendReceiveATcmds(self,cmd): ser.readSerialBuffer() print("send AT command = ", cmd) ser.sendString(cmd) + time.sleep(0.1) j=0 received_data=b"" while (j<3): @@ -280,13 +298,10 @@ def sendReceiveATcmds(self,cmd): def setATcommands(self): self.ser.listenPauseFlag=True - print ("pause standard reading AT started, Set PIN Low (AT enabled) ---------------------------------------------------" ) - # the HC-12 set pin should be put to LOW to enable AT commands - GPIO.setmode(GPIO.BCM) - GPIO.setup(self.SetPIN, GPIO.OUT, initial=GPIO.LOW) # set pin 7 (GPIO4) to OUTPUT (SET pin of HC12) - GPIO.output(self.SetPIN, GPIO.LOW) # set HC12 to AT mode - # pause the receiver based - time.sleep(1) + print ("pause standard reading AT started) ---------------------------------------------------" ) + + self.enableATpin() + print ("AT commands list ",self.ATcommandslist) ATok=True # if one of the AT command is not successful it return false @@ -319,8 +334,7 @@ def setATcommands(self): ATok=False - GPIO.output(self.SetPIN, GPIO.HIGH) # set HC12 to normal mode - time.sleep(1) + self.disableATpin() #restart the receiver self.ser.listenPauseFlag=False print ("remove pause, Set PIN High (AT disabled) ---------------------------------------------------" ) @@ -357,7 +371,9 @@ def __init__(self, dataBuffer, dataManagement): self.datadict=dataManagement.readDataFile() self.dataBuffer=dataBuffer self.key=self.datadict.get("ChiperKey") - self.mediumconnected=False + self.mediumconnected=False + self.ackDict={} + self.sendCommandBusy=False def initRadio(self): @@ -380,7 +396,7 @@ def saveDataFileAndApply(self,datadict): return False def gotdata(self,received_data=""): - #print("hey I got data ", received_data) + print("hey I got data ", received_data) # decript data databytes=self.dechiper(received_data, self.key) datastring="" @@ -393,11 +409,24 @@ def gotdata(self,received_data=""): # when using the lowbitrate long distance mode, only 60 bytes at once can be transmitted. for this reason, Json is not used due to too much overhead datadict=self.extracNameandValue(datastring) #print (datadict) + if datadict: - # confirm the message has been correctly received - # send back message with confirmation - self.sendAck(self.medium.ser,datadict) - self.dataBuffer.addLastData(datadict) + # received message can be a Sensor reading, or an ack sent in response to a command + # if this is an ack, then the "value" of the datadict should be "OK" + if datadict.get("value","")=="OK": + # this is an ack + # save the ACK in ack list + # datadict={"name":datalist[0],"millisindex":datalist[1],"value":datalist[2]} + uniString=datadict.get("name","")+datadict.get("millisindex","") + self.ackDict[uniString]="OK" + + else: + # this is a message and requires an ACK + + # confirm the message has been correctly received + # send back message with confirmation + self.sendAck(self.medium.ser,datadict) + self.dataBuffer.addLastData(datadict) def setmeduimcomm(self): @@ -416,6 +445,7 @@ def dechiper(self, data, key): return bytes(encoded) return data + def extracNameandValue(self, datastr): # the formant {nameID:data:millisindex:} is used. @@ -449,6 +479,72 @@ def sendAck(self, ser, datadict): ser.sendBytes(dataByteschip) return datadict + def createMillisTimestamps(self,strlenght): + millis= round(time.time() * 1000) + millisstr=str(millis) + start=0 + if len(millisstr)>strlenght: + start=len(millisstr)-strlenght + return millisstr[start:] + + + + + def sendCommand(self, topic, value , retry): + # this function sends a command to be transmitted over the HC-12 and waits the ack + # if ACK correct then return True otherwise False + waitinde=60 + while (self.sendCommandBusy) and (waitinde>0): # avoid sending other command if the previous is not finished + time.sleep(0.1) + waitinde=waitinde-1 + if self.sendCommandBusy: + return False, "HC-12 busy" + + self.sendCommandBusy=True + + returnvals = False , "HC-12 not detected" + # check if the medium is connected + if self.mediumconnected: + + + # millisindex is a value to univoquely identify the sent message, and is the number of milliseconds + millisindex=self.createMillisTimestamps(6) + # prepare the string + datastr=bytes("{"+topic+":"+millisindex+":"+value+":}", 'utf-8') + print("Send HC-12 command " , datastr) + dataByteschip=self.dechiper(datastr, self.key) # in this case it chipers + + # manage the retry + inde=0 + while (not returnvals[0]) and (inde0): + time.sleep(0.1) + waitinde=waitinde-1 + print("Waiting ACK " , waitinde) + + if self.ackDict.get(uniString,"")=="OK": + returnvals = True, "ACK OK" + else: + returnvals = False, "No ACK received" + + self.sendCommandBusy=False + return returnvals + + + + if __name__ != '__main__': @@ -473,7 +569,75 @@ def initHC12(): if __name__ == '__main__': + """ + SetPIN=4 + baudRateList=[1200,9600] + # pause the receiver based + # the HC-12 set pin should be put to LOW to enable AT commands + GPIO.setmode(GPIO.BCM) + GPIO.setup(SetPIN, GPIO.OUT, initial=GPIO.LOW) # set pin 7 (GPIO4) to OUTPUT (SET pin of HC12) + GPIO.output(SetPIN, GPIO.LOW) # set HC12 to AT mode + time.sleep(1.5) + + isok=False + for baudrate in baudRateList: + ser=_SerialConnection(baudrate=baudrate) + time.sleep(0.5) + if ser.serialok: + ser.listenPauseFlag=True + inde=2 + isok=False + while (inde>0)and(not isok): + isok=False + #send first AT command just to check the HC-12 is answering + print("Check if AT commands are working") + cmd="AT\n" + isok=False + # empty the serial buffer + ser.readSerialBuffer() + print("send AT command = ", cmd) + ser.sendString(cmd) + j=0 + received_data=b"" + while (j<3): + # wait for data + ser.waitfordata(14) + outok, received_data = ser.readSerialBuffer() + if outok: + if received_data: + print("Received = " , received_data) + print ( "Received = " , received_data.decode('UTF-8')) + isok=True + break + else: + # try to send again the comamand + print("re-send AT command = ", cmd) + ser.sendString(cmd) + print(j, "inside loop Command =",cmd) + j=j+1 + + + + if outok: + if b"ok" in received_data or b"OK" in received_data: + isok=True + print ("Check AT Successfull") + + + + inde=inde-1 + if isok: + break + else: + break + + GPIO.output(SetPIN, GPIO.HIGH) # set HC12 to normal mode + time.sleep(0.5) + # pause the receiver based + ser.listenPauseFlag=False + + """ HC12inst=HC12() SetPIN=4 @@ -485,8 +649,8 @@ def initHC12(): time.sleep(1) HC12inst.sendReceiveATcmds("AT+RB") - time.sleep(0.3) - HC12inst.sendReceiveATcmds("AT+DEFAULT") + #time.sleep(0.3) + #HC12inst.sendReceiveATcmds("AT+DEFAULT") time.sleep(0.3) HC12inst.sendReceiveATcmds("AT+V") @@ -499,7 +663,6 @@ def initHC12(): - """ AT+Cxxx: Change wireless communication channel, selectable from 001 to 127 (for wireless channels exceeding 100, the communication distance cannot be guaranteed). The default value for the wireless channel is 001, with a working frequency of 433.4MHz. The channel stepping is 400KHz, and the working frequency of channel diff --git a/HWcontrol.py b/HWcontrol.py index c9e95c1..9d1b55c 100755 --- a/HWcontrol.py +++ b/HWcontrol.py @@ -98,6 +98,17 @@ def toint(thestring, outwhenfail): except: return outwhenfail + +def returnmsg(recdata,cmd,msg,successful): + recdata.clear() + print(msg) + recdata.append(cmd) + recdata.append(msg) + recdata.append(successful) + if not successful: + logger.error("Error: %s" ,msg) + return True + def read_status_data(data,element,variable): #print data if element in data: @@ -179,9 +190,7 @@ def execute_task(cmd, message, recdata): elif cmd==HWCONTROLLIST[13]: #return zero #print "returnzero" returndata="0" - recdata.append(cmd) - recdata.append(returndata) - recdata.append(1) # confirm data for acknowledge + returnmsg(recdata,cmd,returndata,1) return True elif cmd==HWCONTROLLIST[14]: # stoppulse @@ -227,12 +236,9 @@ def execute_task(cmd, message, recdata): return get_BMP180_data(cmd, message, recdata, "temperature") else: - print("Command not found") - recdata.append(cmd) - recdata.append("e") - recdata.append(0) - return False; - return False; + returnmsg(recdata,cmd,"Command not found",0) + return False + return False def execute_task_fake(cmd, message, recdata): @@ -254,11 +260,8 @@ def execute_task_fake(cmd, message, recdata): return True; else: - print("no fake command available" , cmd) - recdata.append(cmd) - recdata.append("e") - recdata.append(0) - return False; + returnmsg(recdata,cmd,"Fake command not found",0) + return False return True @@ -304,9 +307,7 @@ def get_I2C_devices_list(): def get_DHT22_temperature_fake(cmd, message, recdata, DHT22_data): - recdata.append(cmd) - recdata.append("10.10") - recdata.append(1) + returnmsg(recdata,cmd,"10.10",1) return True @@ -379,18 +380,16 @@ def get_DHT22_reading(cmd, message, recdata, DHT22_data): def get_DHT22_temperature(cmd, message, recdata, DHT22_data): successflag , element=get_DHT22_reading(cmd, message, recdata, DHT22_data) - recdata.append(cmd) - recdata.append(DHT22_data[element]['temperature']) - recdata.append(successflag) + msg=DHT22_data[element]['temperature'] + returnmsg(recdata,cmd,msg,successflag) return DHT22_data[element]['lastupdate'] def get_DHT22_humidity(cmd, message, recdata, DHT22_data): successflag , element=get_DHT22_reading(cmd, message, recdata, DHT22_data) - recdata.append(cmd) - recdata.append(DHT22_data[element]['humidity']) - recdata.append(successflag) + msg=DHT22_data[element]['humidity'] + returnmsg(recdata,cmd,msg,successflag) return DHT22_data[element]['lastupdate'] @@ -440,19 +439,11 @@ def get_BMP180_data(cmd, message, recdata, datatype): except: #print " I2C bus reading error, BMP180 , pressure sensor " - logger.error(" I2C bus reading error, BMP180 sensor %s " , datatype) - print("Error, BMP180 reading " , datatype) - recdata.append(cmd) - recdata.append("I2C bus reading error, BMP180") - recdata.append(0) - - - + msg=" I2C bus reading error, BMP180 sensor " + datatype + returnmsg(recdata,cmd,msg,0) #pressure is in hecto Pascal - recdata.append(cmd) - recdata.append(reading) - recdata.append(successflag) + returnmsg(recdata,cmd,reading,successflag) return True @@ -485,11 +476,7 @@ def get_BME280_data(cmd, message, recdata, datatype): isok, msg = bme280.setup() if not isok: - print(msg) - logger.error(msg) - recdata.append(cmd) - recdata.append(msg) - recdata.append(0) + returnmsg(recdata,cmd,msg,0) return True data_all = bme280.read_all() @@ -506,19 +493,16 @@ def get_BME280_data(cmd, message, recdata, datatype): successflag=1 logger.info("BME280 %s reading: %s", datatype, reading) print("BME280 ", datatype, " reading: ", reading) - recdata.append(cmd) - recdata.append(reading) - recdata.append(successflag) + returnmsg(recdata,cmd,reading,successflag) statusmsg="" recdata.append(statusmsg) return True - print("Error, BME280 reading ") - logger.error("Error, BME reading ") - recdata.append(cmd) - recdata.append("Generic Error, BME280") - recdata.append(0) + + + msg="Generic Error, BME280" + returnmsg(recdata,cmd,msg,0) return True @@ -567,17 +551,11 @@ def get_BH1750_light(cmd, message, recdata): successflag=1 except: #print " I2C bus reading error, BH1750 , light sensor " - logger.error(" I2C bus reading error, BH1750 , light sensor ") - print("Error, I2C bus reading error, BH1750 , light sensor ") - logger.error("Error, BH1750 reading ") - recdata.append(cmd) - recdata.append("Generic Error, BH1750") - recdata.append(0) + msg=" I2C bus reading error, BH1750 , light sensor " + returnmsg(recdata,cmd,msg,0) return True - recdata.append(cmd) - recdata.append(light) - recdata.append(successflag) + returnmsg(recdata,cmd,light,successflag) return True @@ -624,10 +602,8 @@ def get_DS18B20_temperature(cmd, message, recdata): if not isOK: # address of the termometer not found - logger.error("DS18B20 address not found: %s", SensorAddress) - recdata.append(cmd) - recdata.append("DS18B20 address not found") - recdata.append(0) + msg="DS18B20 address not found: " + SensorAddress + returnmsg(recdata,cmd,msg,0) return True @@ -662,9 +638,7 @@ def get_DS18B20_temperature(cmd, message, recdata): #print "error reading the DS18B20" logger.error("error reading the DS18B20") - recdata.append(cmd) - recdata.append(temperature) - recdata.append(successflag) + returnmsg(recdata,cmd,temperature,successflag) return True @@ -704,12 +678,9 @@ def get_HX711_voltage(cmd, message, recdata): if (PINDATA<0)or(PINCLK<0): - print("HX711 PIN not valid", SensorAddress) - # address not correct + msg="HX711 PIN not valid" + SensorAddress logger.error("HX711 PIN not valid: Pindata = %s Pinclk= %s", PINDATA_str,PINCLK_str) - recdata.append(cmd) - recdata.append("HX711 PIN not valid") - recdata.append(0) + returnmsg(recdata,cmd,msg,0) return True reading=0 @@ -737,10 +708,8 @@ def get_HX711_voltage(cmd, message, recdata): #print "HX711 data:",data if inde==0: - logger.error("HX711 reading error") - recdata.append(cmd) - recdata.append("HX711 reading error") - recdata.append(0) + msg="HX711 reading error" + returnmsg(recdata,cmd,msg,0) return True successflag=1 @@ -754,11 +723,7 @@ def get_HX711_voltage(cmd, message, recdata): reading=averagefiltered - print("reading ", reading) - - recdata.append(cmd) - recdata.append(reading) - recdata.append(successflag) + returnmsg(recdata,cmd,reading,successflag) return True def get_SlowWire_reading(cmd, message, recdata): @@ -788,12 +753,9 @@ def get_SlowWire_reading(cmd, message, recdata): if (PINDATA<0): - print("SlowWire PIN not valid") # address not correct - logger.error("SlowWire PIN not valid: Pindata = %s ", PINDATA_str) - recdata.append(cmd) - recdata.append("SlowWire PIN not valid") - recdata.append(0) + msg="SlowWire PIN not valid: Pindata = " + PINDATA_str + returnmsg(recdata,cmd,msg,0) return True reading=0 @@ -826,10 +788,8 @@ def get_SlowWire_reading(cmd, message, recdata): if inde==0: - logger.error("SlowWire reading error") - recdata.append(cmd) - recdata.append("SlowWire reading error") - recdata.append(0) + msg="SlowWire reading error" + returnmsg(recdata,cmd,msg,0) return True successflag=1 @@ -841,11 +801,7 @@ def get_SlowWire_reading(cmd, message, recdata): reading=averagefiltered - print("reading ", reading) - - recdata.append(cmd) - recdata.append(reading) - recdata.append(successflag) + returnmsg(recdata,cmd,reading,successflag) return True @@ -878,12 +834,8 @@ def get_Hygro24_capacity(cmd, message, recdata): else: SensorAddressInt = int(SensorAddress) except: - print("can't parse %s as an i2c address", SensorAddress) - # address not correct - logger.error("Hygro24_I2C address incorrect: %s", SensorAddress) - recdata.append(cmd) - recdata.append("Hygro24_I2C address incorrect") - recdata.append(0) + msg="Hygro24_I2C address incorrect: " + SensorAddress + returnmsg(recdata,cmd,msg,0) return True reading=0 @@ -897,16 +849,11 @@ def get_Hygro24_capacity(cmd, message, recdata): if isOK: successflag=1 else: - logger.error("Hygro24_I2C reading error") - recdata.append(cmd) - recdata.append("Hygro24_I2C reading error") - recdata.append(0) + msg="Hygro24_I2C reading error" + returnmsg(recdata,cmd,msg,0) return True - - recdata.append(cmd) - recdata.append(reading) - recdata.append(successflag) + returnmsg(recdata,cmd,reading,successflag) return True @@ -949,11 +896,8 @@ def get_MCP3008_channel(cmd, message, recdata): if (waittime>=maxwait): #something wrog, wait too long, avoid initiate further processing - print("MCP3008 wait time EXCEEDED ") - logger.warning("Wait Time exceeded, not able to read ADCdata Channel: %d", channel) - recdata.append(cmd) - recdata.append("Wait Time exceeded, not able to read ADCdata Channel: "+str(channel)) - recdata.append(0) + msg="Wait Time exceeded, not able to read ADCdata Channel: "+str(channel) + returnmsg(recdata,cmd,msg,0) return True MCP3008_busy_flag=True @@ -1011,16 +955,11 @@ def get_MCP3008_channel(cmd, message, recdata): except: # this will not work, there is no way to detect if the MCP3008 is attached to SPI interface or not. # MCP3008 has no internal register to interrogate, the - print(" DPI bus reading error, MCP3008 , AnalogDigitalConverter ") - logger.error(" DPI bus reading error, MCP3008 , AnalogDigitalConverter ") - recdata.append(cmd) - recdata.append("Error, DPI bus reading error, MCP3008 , AnalogDigitalConverter") - recdata.append(0) + msg="Error, DPI bus reading error, MCP3008 , AnalogDigitalConverter" + returnmsg(recdata,cmd,msg,0) return True - recdata.append(cmd) - recdata.append(volts) - recdata.append(successflag) + returnmsg(recdata,cmd,volts,successflag) powerPIN_stop(POWERPIN,0) @@ -1217,9 +1156,7 @@ def gpio_pulse(cmd, message, recdata): print("No Action, pulse activated when PIN already active and activationmode is NOADD") logger.warning("No Action, pulse activated when PIN already active and activationmode is NOADD") successflag=1 - recdata.append(cmd) - recdata.append(PIN) - recdata.append(successflag) + returnmsg(recdata,cmd,PIN,successflag) return True @@ -1246,9 +1183,7 @@ def gpio_pulse(cmd, message, recdata): #print "pulse started", time.ctime() , " PIN=", PIN , " Logic=", logic successflag=1 - recdata.append(cmd) - recdata.append(PIN) - recdata.append(successflag) + returnmsg(recdata,cmd,PIN,successflag) return True def gpio_stoppulse(cmd, message, recdata): @@ -1270,9 +1205,7 @@ def gpio_stoppulse(cmd, message, recdata): print("No Action, Already OFF") logger.warning("No Action, Already OFF") successflag=1 - recdata.append(cmd) - recdata.append(PIN) - recdata.append(successflag) + returnmsg(recdata,cmd,PIN,successflag) return True @@ -1282,21 +1215,19 @@ def gpio_stoppulse(cmd, message, recdata): PINthreadID.cancel() endpulse(PIN,logic,POWERPIN) #this also put powerpin off - recdata.append(cmd) - recdata.append(PIN) + returnmsg(recdata,cmd,PIN,1) return True def gpio_pin_level(cmd, message, recdata): msgarray=message.split(":") PIN=msgarray[1] - recdata.append(msgarray[0]) PINlevel=read_status_data(GPIO_data,PIN,"level") if PINlevel is not None: - recdata.append(str(PINlevel)) + returnmsg(recdata,cmd,str(PINlevel),1) return True else: - recdata.append("e") + returnmsg(recdata,cmd,"error",0) return False def get_InterruptFrequency_reading(cmd, message, recdata): @@ -1305,33 +1236,27 @@ def get_InterruptFrequency_reading(cmd, message, recdata): msgarray=message.split(":") #print " read pin input ", message PINstr=msgarray[1] - isRealPIN,PIN=CheckRealHWpin(PINstr) - recdata.append(cmd) + isRealPIN,PIN=CheckRealHWpin(PINstr) if isRealPIN: # here the reading reading=interruptmod.ReadInterruptFrequency(PIN) - recdata.append(reading) - recdata.append(successflag) + returnmsg(recdata,cmd,reading,successflag) else: successflag=0 - recdata.append("e") - recdata.append(successflag) + returnmsg(recdata,cmd,"error",successflag) return True def get_WeatherAPI_reading(cmd, message, recdata): import weatherAPImod - successflag=1 - recdata.append(cmd) + successflag=1 # here the reading isok, reading=weatherAPImod.CalculateRainMultiplier() if isok: - recdata.append(reading) - recdata.append(successflag) + returnmsg(recdata,cmd,reading,successflag) else: successflag=0 - recdata.append("e") - recdata.append(successflag) + returnmsg(recdata,cmd,"error",successflag) return True @@ -1343,19 +1268,17 @@ def read_input_pin(cmd, message, recdata): msgarray=message.split(":") #print " read pin input ", message PINstr=msgarray[1] - isRealPIN,PIN=CheckRealHWpin(PINstr) - recdata.append(cmd) + isRealPIN,PIN=CheckRealHWpin(PINstr) if isRealPIN: if GPIO.input(PIN): reading="1" else: reading="0" - recdata.append(reading) - recdata.append(successflag) + + returnmsg(recdata,cmd,reading,successflag) else: successflag=0 - recdata.append("e") - recdata.append(successflag) + returnmsg(recdata,cmd,"error",successflag) return True @@ -1384,8 +1307,7 @@ def gpio_set_servo(cmd, message, recdata): pwm.stop() time.sleep(0.1) #print "servo set to frequency", frequency , " PIN=", PIN , " Duty cycle=", duty - recdata.append(cmd) - recdata.append(PIN) + returnmsg(recdata,cmd,PIN,1) return True def isPinActive(PIN, logic): @@ -1483,9 +1405,7 @@ def gpio_set_stepper(cmd, message, recdata , stepper_data): #print "stepper: Interface", Interface_Number , " direction=", direction , " speed=", speed , " steps=", steps - recdata.append(cmd) - recdata.append(Interface_Number) - + returnmsg(recdata,cmd,Interface_Number,1) return True @@ -1495,8 +1415,7 @@ def get_stepper_status(cmd, message, recdata , stepper_data): messagelen=len(msgarray) Interface=msgarray[1] returndata=read_status_dict(stepper_data,Interface) - recdata.append(cmd) - recdata.append(returndata) + returnmsg(recdata,cmd,returndata,1) return True def get_hbridge_status(cmd, message, recdata , hbridge_data): @@ -1505,8 +1424,7 @@ def get_hbridge_status(cmd, message, recdata , hbridge_data): messagelen=len(msgarray) Interface=msgarray[1] returndata=read_status_dict(hbridge_data,Interface) - recdata.append(cmd) - recdata.append(returndata) + returnmsg(recdata,cmd,returndata,1) return True diff --git a/MQTTcontrol.py b/MQTTcontrol.py index 6dfb5b1..c02057d 100644 --- a/MQTTcontrol.py +++ b/MQTTcontrol.py @@ -35,8 +35,6 @@ # status variables - - GPIO_data={} GPIO_data["default"]={"level":0, "state":None, "threadID":None} @@ -67,6 +65,15 @@ def tonumber(thestring, outwhenfail): except: return outwhenfail +def returnmsg(recdata,cmd,msg,successful): + recdata.clear() + print(msg) + recdata.append(cmd) + recdata.append(msg) + recdata.append(successful) + if not successful: + logger.error("Error: %s" ,msg) + return True def execute_task(cmd, message, recdata): global hbridge_data @@ -86,7 +93,7 @@ def execute_task(cmd, message, recdata): return readinput_MQTT(cmd, message, recdata) elif cmd==HWCONTROLLIST[3]: # pinstate - return gpio_pin_level(cmd, message, recdata) + return MQTT_pin_level(cmd, message, recdata) elif cmd==HWCONTROLLIST[4]: #hbridge return "" @@ -94,47 +101,29 @@ def execute_task(cmd, message, recdata): else: - print("Command not found") - recdata.append(cmd) - recdata.append("e") - recdata.append(0) - return False; - return False; + returnmsg(recdata,cmd,"Command not Found",0) + return False + return False def execute_task_fake(cmd, message, recdata): if cmd==HWCONTROLLIST[0]: - gpio_pulse(cmd, message, recdata) - return True; + print("fake command") + return True else: - print("no fake command available" , cmd) - recdata.append(cmd) - recdata.append("e") - recdata.append(0) - return False; + msg="no fake command available" + cmd + returnmsg(recdata,cmd,msg,0) + return False return True -def gpio_pin_level(cmd, message, recdata): - msgarray=message.split(":") - PIN=msgarray[1] - recdata.append(msgarray[0]) - PINlevel=statusdataDBmod.read_status_data(GPIO_data,PIN,"level") - if PINlevel is not None: - recdata.append(str(PINlevel)) - return True - else: - recdata.append("e") - return False - - def DictPath_SearchReplace(searchpathlist,jsondata,newvalue): # parse for sinlge param item @@ -221,12 +210,8 @@ def readinput_MQTT(cmd, message, recdata): if (Topic==""): - print("Error, MQTT reading no topic defined", Topic) - # address not correct - logger.error("MQTT reading no topic defined %s", Topic) - recdata.append(cmd) - recdata.append("Topic not configured ") - recdata.append(0) + msg="Error, MQTT reading no topic defined " +Topic + returnmsg(recdata,cmd,msg,0) return True # Get the last value pubished for this topic. @@ -244,12 +229,8 @@ def readinput_MQTT(cmd, message, recdata): if jsonstring=="": - print("Error, MQTT reading no Reading for the topic ", subtopic) - # address not correct - logger.error("MQTT reading no Reading for the topic %s", subtopic) - recdata.append(cmd) - recdata.append("Error, MQTT no Reading for the topic " + subtopic) - recdata.append(0) + msg="Error, MQTT reading no Reading for the topic "+ subtopic + returnmsg(recdata,cmd,msg,0) return True deltaseconds=(datetime.utcnow()-timestamp).total_seconds() @@ -258,12 +239,8 @@ def readinput_MQTT(cmd, message, recdata): if deltaseconds<0: #MQTT reading happenend in the future .... somethign wrong, issue error - print("Error, MQTT reading in the future ") - # address not correct - logger.error("MQTT reading in the future") - recdata.append(cmd) - recdata.append("Error, MQTT reading in the future ...") - recdata.append(0) + msg="Error, MQTT reading in the future " + returnmsg(recdata,cmd,msg,0) return True @@ -284,12 +261,8 @@ def readinput_MQTT(cmd, message, recdata): if isok: if result=="": - print("Error, MQTT no updates from the sensor since ", str(deltaseconds) , " (sec)") - # address not correct - logger.error("MQTT no updates from the sensor %s (sec)", str(deltaseconds)) - recdata.append(cmd) - recdata.append("Error, MQTT no updates from the sensor") - recdata.append(0) + msg="Error, MQTT no updates from the sensor since "+ str(deltaseconds) + "(sec)" + returnmsg(recdata,cmd,msg,0) return True else: @@ -297,9 +270,7 @@ def readinput_MQTT(cmd, message, recdata): successflag=1 logger.info("MQTT input reading: %s", reading) print("MQTT input reading ", reading) - recdata.append(cmd) - recdata.append(reading) - recdata.append(successflag) + returnmsg(recdata,cmd,reading,successflag) statusmsg="MQTT last update " + '{:.1f}'.format(deltaseconds) + " seconds " recdata.append(statusmsg) return True @@ -307,10 +278,8 @@ def readinput_MQTT(cmd, message, recdata): print("Error, MQTT reading ", jsonstring) logger.error("MQTT reading %s", jsonstring) - recdata.append(cmd) - recdata.append("Generic Error, MQTT reading") - recdata.append(0) - + msg="Generic Error, MQTT reading" + returnmsg(recdata,cmd,msg,0) return True @@ -525,12 +494,9 @@ def MQTT_pulse(cmd, message, recdata): if title=="": - print("missing topic") - logger.error("No topic specified, please insert it in the Title field") successflag=0 - recdata.append("e") - recdata.append(successflag) - recdata.append("Missing MQTT topic") + msg="Missing MQTT topic" + returnmsg(recdata,cmd,msg,successflag) return recdata @@ -538,7 +504,7 @@ def MQTT_pulse(cmd, message, recdata): # start pulse activation logic - REGID=MQTT_CMD["ID"] + REGID=MQTT_CMD["ID"] # this is made by the string "topic"+"PIN" ignorepowerpincount=False # in case another timer is active on this TOPIC ID it means that the PIN is activated PINthreadID=statusdataDBmod.read_status_data(GPIO_data,REGID,"threadID") @@ -547,9 +513,7 @@ def MQTT_pulse(cmd, message, recdata): # pin already active if activationmode=="NOADD": # no action needed successflag=1 - recdata.append(cmd) - recdata.append(PIN) - recdata.append(successflag) + returnmsg(recdata,cmd,PIN,successflag) return True PINthreadID.cancel() # cancel thread @@ -575,9 +539,7 @@ def MQTT_pulse(cmd, message, recdata): successflag=1 - recdata.append(cmd) - recdata.append(PIN) - recdata.append(successflag) + returnmsg(recdata,cmd,PIN,successflag) return True def MQTT_stoppulse(cmd, message, recdata): # when ON send MQTT message with the duration in seconds of the activation, and when OFF send zero. @@ -606,11 +568,10 @@ def MQTT_stoppulse(cmd, message, recdata): # when ON send MQTT message with th title=msgarray[8] if title=="": - print("missing topic") + msg="missing topic in Title field" logger.error("No topic specified, please insert it in the Title field") successflag=0 - recdata.append("e") - recdata.append(successflag) + returnmsg(recdata,cmd,msg,successflag) return recdata MQTT_CMD, MQTT_CMD_PWR = create_pulse_CMD_list(PIN,POWERPIN,title,0) @@ -622,46 +583,22 @@ def MQTT_stoppulse(cmd, message, recdata): # when ON send MQTT message with th PINthreadID.cancel() endpulse(MQTT_CMD, MQTT_CMD_PWR,address) #this also put powerpin off - recdata.append(cmd) - recdata.append(PIN) + returnmsg(recdata,cmd,PIN,1) return True def MQTT_pin_level(cmd, message, recdata): msgarray=message.split(":") PIN=msgarray[1] - recdata.append(msgarray[0]) PINlevel=statusdataDBmod.read_status_data(GPIO_data,PIN,"level") if PINlevel is not None: - recdata.append(str(PINlevel)) + returnmsg(recdata,cmd,str(PINlevel),1) return True else: - recdata.append("e") + returnmsg(recdata,cmd,"error",0) return False -def read_input_pin(cmd, message, recdata): - - # this is useful for the real time reading. Suggest to put in oneshot for the hardware configuration because the database record timing is given by the interrupts - successflag=1 - msgarray=message.split(":") - #print " read pin input ", message - PINstr=msgarray[1] - isRealPIN,PIN=CheckRealHWpin(PINstr) - recdata.append(cmd) - if isRealPIN: - if GPIO.input(PIN): - reading="1" - else: - reading="0" - recdata.append(reading) - recdata.append(successflag) - else: - successflag=0 - recdata.append("e") - recdata.append(successflag) - return True - def isPinActive(PIN): @@ -688,7 +625,7 @@ def sendcommand(cmd, message, recdata): def connecttobroker(address,port,timeout=180): - client = mqtt.Client() + client = MQTTutils.Client() client.connect(address, port, timeout) client.loop_forever() diff --git a/advancedmod.py b/advancedmod.py index ca7499a..5389491 100755 --- a/advancedmod.py +++ b/advancedmod.py @@ -2,9 +2,9 @@ """ watering UI setting storage utilities """ -from __future__ import print_function +from __future__ import print_function -from builtins import str +from builtins import str import logging import os import os.path @@ -22,7 +22,7 @@ DATAFILENAME="addata.txt" DEFDATAFILENAME="default/defaddata.txt" -global data + data=[] tempelementdict={} @@ -48,6 +48,11 @@ # filestoragemod.savechange(filename,searchfield,searchvalue,fieldtochange,newvalue) # filestoragemod.deletefile(filename) +def readfromfile(): + global data + filestoragemod.readfiledata(DATAFILENAME,data) + + def replaceschemanameandsave(replacedict): filename=DATAFILENAME filedata=[] @@ -100,7 +105,30 @@ def getparamlist(): return datalist -def getelementlist(): + +def getSelCycleOpt(default=""): + recordkey="cycleOption" + datadict={} + for ln in data: + if recordkey in ln: + datadict[ln["name"]]=ln[recordkey] + else: + datadict[ln["name"]]=default + return datadict + +def getSelDayCycle(default=""): + recordkey="dayCycle" + datadict={} + for ln in data: + if recordkey in ln: + datadict[ln["name"]]=ln[recordkey] + else: + datadict[ln["name"]]=default + return datadict + + + +def getelementlist(): # return the schemas ordered recordkey="name" recordvalue="listelements" datalist=[] @@ -138,7 +166,9 @@ def getrowdata(recordvalue,paramlist): for ln in data: if ln[recordkey]==recordvalue: for param in paramlist: - datalist.append((ln[param])) + if param in ln: + datalist.append((ln[param])) + return datalist def gettable(): @@ -151,13 +181,16 @@ def gettable(): return datalist + def replacerow(element,dicttemp): + global data searchfield="name" searchvalue=element for line in data: if line[searchfield]==searchvalue: - for row in line: - line[row]=dicttemp[row] + data.remove(line) + data.append(dicttemp) + #print (" data ", data) filestoragemod.savefiledata(DATAFILENAME,data) return True return False diff --git a/autofertilizermod.py b/autofertilizermod.py index 0e6c935..82f9b02 100755 --- a/autofertilizermod.py +++ b/autofertilizermod.py @@ -62,7 +62,7 @@ def checkactivate(elementwater,durationwater): def activatedoser(element, duration): print(element, " ",duration, " " , datetime.now()) logger.info('Doser Pulse, pulse time for ms = %s', duration) - pulseok=hardwaremod.makepulse(element,duration) + msg , pulseok=hardwaremod.makepulse(element,duration) # salva su database actuatordbmod.insertdataintable(element,duration) # put flag down diff --git a/automationmod.py b/automationmod.py index 5c4ff84..59d6cbf 100755 --- a/automationmod.py +++ b/automationmod.py @@ -16,6 +16,7 @@ import statusdataDBmod import math import threading +import ActuatorControllermod logger = logging.getLogger("hydrosys4."+__name__) @@ -222,7 +223,7 @@ def CheckActivateNotify(element,waitingtime,value,mailtype,sensor,sensorvalue): # action print("Implement Actuator Value ", value) logger.info('Procedure to start actuator %s, for value = %s', element, value) - isok=activateactuator(element, value) + msg , isok=activateactuator(element, value) # invia mail, considered as info, not as alert if (mailtype!="warningonly")and(mailtype!="none"): @@ -237,67 +238,8 @@ def CheckActivateNotify(element,waitingtime,value,mailtype,sensor,sensorvalue): - - def activateactuator(target, value): # return true in case the state change: activation is >0 or a different position from prevoius position. - # check the actuator - isok=False - actuatortype=hardwaremod.searchdata(hardwaremod.HW_INFO_NAME,target,hardwaremod.HW_CTRL_CMD) - actuatortypelist=actuatortype.split("/") - if actuatortypelist: - actuatortype=actuatortypelist[0] - print (" Automation Actuator " + actuatortype + " target " + target) - supportedactuators=["pulse","servo","stepper"] - # stepper motor - if actuatortype=="stepper": - out, isok = hardwaremod.GO_stepper_position(target,value) - if isok: - actuatordbmod.insertdataintable(target,value) - - # hbridge motor - if actuatortype=="hbridge": - out, isok = hardwaremod.GO_hbridge_position(target,value) - if isok: - actuatordbmod.insertdataintable(target,value) - - # pulse - if actuatortype=="pulse": - duration=hardwaremod.toint(value,0) - # check the fertilizer doser flag before activating the pulse - doseron=autofertilizermod.checkactivate(target,duration) - # start pulse - pulseok=hardwaremod.makepulse(target,duration) - # salva su database - if "Started" in pulseok: - actuatordbmod.insertdataintable(target,duration) - isok=True - - # servo motor - if actuatortype=="servo": - out, isok = hardwaremod.servoangle(target,value,0.5) - if isok: - actuatordbmod.insertdataintable(target,value) - - # photo - if actuatortype=="photo": - duration=hardwaremod.toint(value,0) - if duration>0: - isok=hardwaremod.takephoto(True) - # save action in database - if isok: - actuatordbmod.insertdataintable(target,1) - - # mail - if (actuatortype=="mail+info+link")or(actuatortype=="mail+info"): - if value>0: - mailtext=str(value) - isok=emailmod.sendmail(target,"info","Automation Value:" + mailtext) - # save action in database - if isok: - actuatordbmod.insertdataintable(target,1) - - - return isok + return ActuatorControllermod.activateactuator(target,value) def isNowInTimePeriod(startTime, endTime, nowTime): diff --git a/autowateringmod.py b/autowateringmod.py index 35dc816..4fa0c10 100755 --- a/autowateringmod.py +++ b/autowateringmod.py @@ -643,11 +643,11 @@ def activatewater(element, duration): # check the activation of the doser before the pump doseron=autofertilizermod.checkactivate(element,duration) # this has a blocking sleep command #activate pump - pulseok=hardwaremod.makepulse(element,duration) + msg , pulseok=hardwaremod.makepulse(element,duration) # salva su database - if "Started" in pulseok: + if pulseok: actuatordbmod.insertdataintable(element,duration) - return pulseok + return msg if __name__ == '__main__': diff --git a/bentornado.py b/bentornado.py old mode 100644 new mode 100755 index 06aa6da..1b8aaae --- a/bentornado.py +++ b/bentornado.py @@ -1,9 +1,13 @@ from tornado.wsgi import WSGIContainer from tornado.httpserver import HTTPServer from tornado.ioloop import IOLoop +from networkmod import LOCALPORT from start import application -import networkmod + http_server = HTTPServer(WSGIContainer(application)) -http_server.listen(networkmod.LOCALPORT) + +http_server.listen(LOCALPORT) + IOLoop.instance().start() + diff --git a/changelog/change b/changelog/change index 20b310c..d5f39c0 100755 --- a/changelog/change +++ b/changelog/change @@ -860,6 +860,47 @@ Verified clock setting behavior, when the Internet connection is present the set - Fix crash when Serial is not enabled - Added the Hardware setup refresh button +2021-08-09 -> release 332c + +- Add scheduling watering for option for N Day irrigation + +2021-08-10 -> release 332d + +- buf fixing +- Change the menu from Message to notification +- Reverse the order the notifications appear +- add note to the MqTT address info +- add the possibility to use other actuator in the WateringPlan +- Multiply the watering delay by the Waterdrop number + +2021-08-10 -> release 332e + +- buf fixing - adjust schema number + +2021-08-16 -> release 333 + +- Change the MasterScheduler start at 00:01:00 from 00:05:00 +- Add the read advancedmod to the readall function + +2021-08-16 -> release 334 + +- minor modification to bentornado.py +- HC-12 output commands for pulse implemented (not tested) + +2021-08-16 -> release 334a + +- bug fixing + +2021-08-18 -> release 334b + +- increase the ACK wait time in the HC12 + +2021-08-30 -> release 335 + +- Static IP address procedure, added the option not to force the static IP address (option in network advanced) + + + ------- Future releases: ----------- NOTE: diff --git a/filestoragemod.py b/filestoragemod.py index a09ff3a..270ad4b 100755 --- a/filestoragemod.py +++ b/filestoragemod.py @@ -2,8 +2,8 @@ """ file storage utility """ -from __future__ import print_function -from builtins import range +from __future__ import print_function +from builtins import range import basicSetting import logging import os @@ -159,6 +159,7 @@ def savefiledata(filename,filedata): for line in filedata: #jsonStr=json.dumps(line, sort_keys=True, indent=14) jsonStr=json.dumps(line, sort_keys=True) + #print ( " Jsonstring ", jsonStr) out_file.write(jsonStr) out_file.write("\n") out_file.close() diff --git a/hardwaremod.py b/hardwaremod.py index 90521f3..f20bf3c 100755 --- a/hardwaremod.py +++ b/hardwaremod.py @@ -299,6 +299,12 @@ def sendcommand(cmd,sendstring,recdata,target="", priority=0): return HC12control.sendcommand(cmd,sendstring,recdata) elif cmd in GPIOEXPI2Ccontrol.HWCONTROLLIST: return GPIOEXPI2Ccontrol.sendcommand(cmd,sendstring,recdata) + else: + successflag=0 + recdata.append(cmd) + recdata.append("Device not found") + recdata.append(successflag) + return True else: successflag=0 recdata.append(cmd) @@ -448,7 +454,7 @@ def activatepulse(command,PIN,duration,activationmode,target,priority): testpulsetime=str(testpulsetimeint) # durantion in seconds except ValueError: print(" No valid data or zero ", target) - return "error" + return "Pulse Duration not valid" , False # normal pulse @@ -463,20 +469,27 @@ def activatepulse(command,PIN,duration,activationmode,target,priority): while (not(isok))and(i<2): i=i+1 recdata=[] - ack= sendcommand(command,sendstring,recdata,target,priority) + ack = sendcommand(command,sendstring,recdata,target,priority) #print "returned data " , recdata # recdata[0]=command (string), recdata[1]=data (string) , recdata[2]=successflag (0,1) - if ack and recdata[2]: - #print target, "correctly activated" + successflag=0 + if len(recdata)>2: + successflag=recdata[2] + msg="" + if len(recdata)>1: + msg=recdata[1] + + if ack and successflag: isok=True - return "Pulse Started" + recdata.append("Pulse Started") + return "Pulse Started", True else: - if len(recdata)>2: - if not recdata[2]: - return recdata[1] + if not successflag: + print ( " Return Status ", msg) + return msg , False - return "error" + return "Generic error", False def stoppulse(target): diff --git a/interruptmod.py b/interruptmod.py index 79852ba..38ff69a 100755 --- a/interruptmod.py +++ b/interruptmod.py @@ -16,6 +16,7 @@ import statusdataDBmod import threading import time as t +import ActuatorControllermod @@ -503,7 +504,7 @@ def CheckActivateNotify(element,sensor,preemptiontime,actuatoroutput,actionmodea print("Implement Actuator Value ", value) logger.info('Procedure to start actuator %s, for value = %s', element, value) - isok=activateactuator(element, value) + msg , isok=activateactuator(element, value) if isok: statusdataDBmod.write_status_data(AUTO_data,element,"lasteventtime",datetime.utcnow()) @@ -571,7 +572,7 @@ def CheckandFollowup(element): # followup action print("Implement Actuator Value followup", value) logger.info('Procedure to start actuator followup %s, for value = %s', element, value) - isok=activateactuator(element, value) + msg , isok=activateactuator(element, value) if isok: statusdataDBmod.write_status_data(AUTO_data,element,"lastactiontime",datetime.utcnow()) @@ -738,65 +739,8 @@ def saveblocking(sensor,cleanThreadID=True): SAVEBLOCKINGBUSY=False - def activateactuator(target, value): # return true in case the state change: activation is >0 or a different position from prevoius position. - # check the actuator - isok=False - actuatortype=hardwaremod.searchdata(hardwaremod.HW_INFO_NAME,target,hardwaremod.HW_CTRL_CMD) - supportedactuators=["pulse","servo","stepper"] - # stepper motor - if actuatortype=="stepper": - out, isok = hardwaremod.GO_stepper_position(target,value,priority=ACTIONPRIORITYLEVEL) - if isok: - actuatordbmod.insertdataintable(target,value) - - # hbridge motor - if actuatortype=="hbridge": - out, isok = hardwaremod.GO_hbridge_position(target,value) - if isok: - actuatordbmod.insertdataintable(target,value) - - # pulse - if actuatortype=="pulse": - duration=hardwaremod.toint(value,0) - if duration>0: - # check the fertilizer doser flag before activating the pulse - doseron=autofertilizermod.checkactivate(target,duration) - # start pulse - pulseok=hardwaremod.makepulse(target,duration,priority=ACTIONPRIORITYLEVEL) - # salva su database - if "Started" in pulseok: - actuatordbmod.insertdataintable(target,duration) - isok=True - else: - pulseok=hardwaremod.stoppulse(target) - - # servo motor - if actuatortype=="servo": - out, isok = hardwaremod.servoangle(target,value,0.5,priority=ACTIONPRIORITYLEVEL) - if isok: - actuatordbmod.insertdataintable(target,value) - - # photo - if actuatortype=="photo": - duration=hardwaremod.toint(value,0) - if duration>0: - isok=hardwaremod.takephoto(True) # True override the daily activation - # save action in database - if isok: - actuatordbmod.insertdataintable(target,1) - - # mail - if (actuatortype=="mail+info+link")or(actuatortype=="mail+info"): - if value>0: - mailtext=str(value) - isok=emailmod.sendmail(target,"info","Interrupt Value:" + mailtext) - # save action in database - if isok: - actuatordbmod.insertdataintable(target,1) - - - return isok + return ActuatorControllermod.activateactuator(target,value) def isNowInTimePeriod(startTime, endTime, nowTime): diff --git a/networkdbmod.py b/networkdbmod.py index 946f608..010209f 100755 --- a/networkdbmod.py +++ b/networkdbmod.py @@ -2,7 +2,7 @@ """ fertilizer UI setting storage utilities """ -from __future__ import print_function +from __future__ import print_function import logging import os @@ -117,7 +117,16 @@ def getCUSTOMURL(): dataitem=filestoragemod.searchdata(DATAFILENAME,recordkey,recordvalue,keytosearch) return dataitem - +def getForceStaticIP(): + recordkey="name" + recordvalue="IPsetting" + keytosearch="forceStaticIP" + dataitem=filestoragemod.searchdata(DATAFILENAME,recordkey,recordvalue,keytosearch) + if dataitem=="": + dataitem="True" + return dataitem + + def changesavesetting(FTparameter,FTvalue): searchfield="name" searchvalue="IPsetting" diff --git a/networkmod.py b/networkmod.py index d7c6fa2..056c2ce 100755 --- a/networkmod.py +++ b/networkmod.py @@ -1,7 +1,7 @@ -from __future__ import print_function -from future import standard_library -standard_library.install_aliases() -from builtins import str +from __future__ import print_function +from future import standard_library +standard_library.install_aliases() +from builtins import str import logging import subprocess import threading @@ -34,6 +34,7 @@ IPADDRESS =networkdbmod.getIPaddress() EXTERNALIPADDR="" DHCP_COUNTER=0 +FORCESTATICIP=networkdbmod.getForceStaticIP() def getCUSTOMURL(): return networkdbmod.getCUSTOMURL() @@ -465,7 +466,7 @@ def checkGWsubnet(interface): #------------------- cmd = ['ip', 'route'] ifup_output="" try: - time.sleep(4) + time.sleep(1) ifup_output = subprocess.check_output(cmd).decode('utf-8') time.sleep(0.5) except: @@ -795,7 +796,8 @@ def applyparameterschange(newlocalwifisystem, newpassword, newIPaddress): logger.info('NO connected SSID') print("Connected to the SSID ", ssid) logger.info('Connected SSID: %s -- ', ssid) - addIP("wlan0") + if FORCESTATICIP=="True": + addIP("wlan0") else: print(" No need WiFi restart") @@ -897,7 +899,8 @@ def connect_network(internetcheck=False, backtoAP=False): logger.info('NO connected SSID') print("Connected to the SSID ", ssid) logger.info('Connected SSID: %s -- ', ssid) - addIP("wlan0") + if FORCESTATICIP=="True": + addIP("wlan0") else: print("already connected to the SSID ", ssid) diff --git a/selectedplanmod.py b/selectedplanmod.py index 3086672..997c728 100755 --- a/selectedplanmod.py +++ b/selectedplanmod.py @@ -2,12 +2,12 @@ """ selected plan utility """ -from __future__ import print_function -from __future__ import division +from __future__ import print_function +from __future__ import division -from builtins import str -from builtins import range -from past.utils import old_div +from builtins import str +from builtins import range +from past.utils import old_div import logging import os import os.path @@ -33,9 +33,10 @@ import basicSetting import weatherAPImod import wateringplansensordbmod +import ActuatorControllermod DEBUGMODE=basicSetting.data["DEBUGMODE"] -MASTERSCHEDULERTIME="00:05:00" +MASTERSCHEDULERTIME="00:01:00" STOREPASTDAYS="364" # ///////////////// -- GLOBAL VARIABLES AND INIZIALIZATION --- ////////////////////////////////////////// @@ -60,13 +61,12 @@ def activateandregister(target,activationseconds): # function to activate the actuators duration=hardwaremod.toint(activationseconds,0) print(target, " ",duration, " " , datetime.now()) - logger.info('Pulse time for sec = %s', duration) + logger.info('Activate for Value = %s', duration) # start pulse - pulseok=hardwaremod.makepulse(target,duration) - # salva su database - if "Started" in pulseok: - actuatordbmod.insertdataintable(target,duration) - return pulseok + #msg,pulseok=hardwaremod.makepulse(target,duration) + # the above is replaced by a more generic method to activate actuators + msg , pulseok=ActuatorControllermod.activateactuator(target,duration) # it also save in database + return msg def pulsenutrient(target,activationseconds): #scheduled doser activity for fertilizer duration=hardwaremod.toint(activationseconds,0) @@ -656,9 +656,9 @@ def mastercallback(fromscheduledtime=False): #print paramlist #print elementlistly #print table - paramlistdrop= advancedmod.getparamlist() # day of the week - elementlistdrop= advancedmod.getelementlist() # drops ordered - tabledrop=advancedmod.gettable() # table, each row is a schema number (drop number), each column is a weekday + + waterSchemaList=advancedmod.getelementlist() + tabledrop=advancedmod.gettable() # table, each row is a schema number (drop number), each column is a weekday, return ofject is list with Time and duration for pumpnumber in range(len(elementlist)): #print "number =",pumpnumber @@ -680,18 +680,40 @@ def mastercallback(fromscheduledtime=False): if waterdropnumber>0: #print " month " , month, " drop " , waterdropnumber calltype=hardwaremod.searchdata(hardwaremod.HW_INFO_NAME,pumpname,hardwaremod.HW_FUNC_SCHEDTYPE) - for todayevent in tabledrop[waterschemanumber-1][weekday]: - - timelist=hardwaremod.separatetimestringint(todayevent[0]) - timelist[2]=timelist[2]+watertimedelaysec - argument=[] - argument.append(pumpname) - durationinseconds=hardwaremod.toint(todayevent[1],0)*waterdropnumber - argument.append(durationinseconds) - for i in range(2,len(todayevent)): - argument.append(todayevent[i]) - if durationinseconds>0: #check if the duration in second is >0 - setschedulercallback(calltype,timelist,argument,callback,pumpname) + + # have to verify if the Schema is relevant ot Weekly cycle of daily cycle. + + waterschema=waterSchemaList[int(waterschemanumber-1)] + cycledatalist=advancedmod.getrowdata(waterschema,["cycleOption","dayCycle","startDate"]) + rightDay=True + if len(cycledatalist)>2: + if cycledatalist[0]=="Daily Setting": + rightDay=False + # now it is required to understand if the event can be triggered using the start date adn daycycle + # the weekday to be used is the day=0 (Monday) + startdate=datetime.strptime(cycledatalist[2], "%d/%m/%Y") + today=datetime.now() + deltadays = (today - startdate).days + if deltadays>0: + modulo=deltadays % hardwaremod.toint(cycledatalist[1],1) + print ("Element ", pumpname , "Schema ", waterschema , " Modulo " , modulo) + if modulo==0: + weekday=0 # choose the first table + rightDay=True + + if rightDay: + for todayevent in tabledrop[waterschemanumber-1][weekday]: # given schema and weekday, provide the list of actions + + timelist=hardwaremod.separatetimestringint(todayevent[0]) + timelist[2]=timelist[2]+watertimedelaysec*waterdropnumber + argument=[] + argument.append(pumpname) + durationinseconds=hardwaremod.toint(todayevent[1],0)*waterdropnumber + argument.append(durationinseconds) + for i in range(2,len(todayevent)): + argument.append(todayevent[i]) + if durationinseconds>0: #check if the duration in second is >0 + setschedulercallback(calltype,timelist,argument,callback,pumpname) logger.info('Start other scheduler activities - doser') @@ -781,7 +803,7 @@ def setschedulercallback(calltype,timelist,argument,callbackname,jobname): thedate=clockmod.convertLOCtoUTC_datetime(thedateloc) if len(argument)>0: - print("date ", thedate , " callbackname " , callbackname , " Pump line ", argument[0]) + print("date ", thedate , " callbackname " , callbackname , " Element ", argument[0]) else: print("date ", thedate , " callbackname " , callbackname) try: diff --git a/start.py b/start.py index f7c9c54..67fea0c 100755 --- a/start.py +++ b/start.py @@ -3,7 +3,7 @@ from builtins import str from builtins import range -Release="3.32b" +Release="3.35" #--------------------- from loggerconfig import LOG_SETTINGS @@ -79,11 +79,7 @@ import messageboxmod import wateringplansensordbmod from HC12mod import HC12radionet - - - -# Raspberry Pi camera module (requires picamera package) -from camera_pi import Camera +from camera_pi import Camera # Raspberry Pi camera module (requires picamera package) # ///////////////// -- GLOBAL VARIABLES AND INIZIALIZATION --- ////////////////////////////////////////// application = Flask(__name__) @@ -140,6 +136,7 @@ def runallreadfile(): interruptdbmod.readfromfile() fertilizerdbmod.readfromfile() autofertilizerdbmod.readfromfile() + advancedmod.readfromfile() return True @@ -1439,7 +1436,7 @@ def networksetting(): AP_TIME=request.form['AP_TIME'] WIFIENDIS=request.form['WIFIENDIS'] HOSTNAME=request.form['HOSTNAME'] - + forceStaticIP=request.form['forceStaticIP'] # Check @@ -1471,7 +1468,9 @@ def networksetting(): networkdbmod.changesavesetting('LocalAPSSID',AP_SSID) networkdbmod.changesavesetting('APtime',AP_TIME) networkdbmod.changesavesetting('WIFIENDIS',WIFIENDIS) - + networkdbmod.changesavesetting('forceStaticIP',forceStaticIP) + networkmod.FORCESTATICIP=forceStaticIP + # save and change values in the HOSTAPD config file sysconfigfilemod.hostapdsavechangerow("ssid",AP_SSID) if AP_PASSWORD!=Fake_password: @@ -1496,6 +1495,7 @@ def networksetting(): networkmod.applyparameterschange(AP_SSID, AP_PASSWORD, IPADDRESS) networkmod.WAITTOCONNECT=AP_TIME networkmod.WIFIENDIS=WIFIENDIS + # Change hostapd file first row with HERE data=[] @@ -1528,6 +1528,7 @@ def networksetting(): AP_SSID=networkmod.localwifisystem AP_TIME=str(networkmod.WAITTOCONNECT) WIFIENDIS=networkmod.WIFIENDIS + forceStaticIP=networkmod.FORCESTATICIP connectedssidlist=networkmod.connectedssid() if len(connectedssidlist)>0: connectedssid=connectedssidlist[0] @@ -1537,7 +1538,7 @@ def networksetting(): - return render_template('networksetting.html', IPADDRESS=IPADDRESS, AP_SSID=AP_SSID, AP_PASSWORD=AP_PASSWORD, AP_TIME=AP_TIME , HOSTNAME=HOSTNAME, WIFIENDIS=WIFIENDIS) + return render_template('networksetting.html', IPADDRESS=IPADDRESS, AP_SSID=AP_SSID, AP_PASSWORD=AP_PASSWORD, AP_TIME=AP_TIME , HOSTNAME=HOSTNAME, WIFIENDIS=WIFIENDIS, forceStaticIP=forceStaticIP) @@ -2491,7 +2492,7 @@ def advanced(): if actiontype == "save": - elementnum=request.form['element'] + elementnum=request.form['element'] # this returns the Actuator in row_i number element=elementlist[int(elementnum)] print("save advanced form...:" , element) @@ -2510,7 +2511,7 @@ def advanced(): dicttemp={} dicttemp["name"]=element # table includes all the elements: Element, Parameters, rows, columns - # element is fixed and not relevent for save procedure + # element is fixed and not relevent to save procedure a=-1 for param in table[int(elementnum)]: a=a+1 @@ -2528,13 +2529,17 @@ def advanced(): listrowtemp.append(listcoltemp) dicttemp[paramlist[a]]= listrowtemp + dicttemp["cycleOption"] =request.form[str(elementnum)+"cycleOption"] + dicttemp["dayCycle"]=request.form[str(elementnum)+"dayCycle"] + dicttemp["startDate"]=datetime.now().strftime("%d/%m/%Y") - #print "dicttemp ", dicttemp + #print( "dicttemp ", dicttemp ) advancedmod.replacerow(element,dicttemp) # reset the scheduler - selectedplanmod.resetmastercallback() print("Table saved") - flash('Table has been saved') + selectedplanmod.resetmastercallback() + print("Schedule has been updated") + flash('Schedule has been updated') table=advancedmod.gettable() #print "after",table @@ -2549,11 +2554,19 @@ def advanced(): if actiontype == "goback": print("open watering plan setting") return redirect('/wateringplan/') - - - - - return render_template("advanced.html", title=title,paramlist=paramlist,elementlist=elementlist,table=table,tablehead=tablehead,selectedelement=selectedelement) + + + + cycleOptionList = ["Weekly Setting","Daily Setting"] + dayCycleList = ["1","2","3","4","5","6","7","8","9","10"] + + selCycleOption=advancedmod.getSelCycleOpt(default=cycleOptionList[0]) + selDayCycle=advancedmod.getSelDayCycle(default=dayCycleList[0]) + + print("********* ----" , selCycleOption) + print(selDayCycle) + + return render_template("advanced.html", title=title,cycleOptionList=cycleOptionList,dayCycleList=dayCycleList,selCycleOption=selCycleOption,selDayCycle=selDayCycle,paramlist=paramlist,elementlist=elementlist,table=table,tablehead=tablehead,selectedelement=selectedelement) @@ -3336,7 +3349,7 @@ def weatherAPI(): -@application.route('/message/' , methods=['GET','POST']) +@application.route('/notification/' , methods=['GET','POST']) def messagebox(): if request.method == 'POST': actiontype=request.form['actionbtn'] @@ -3345,6 +3358,7 @@ def messagebox(): posts=messageboxmod.GetMessages() + print(posts) return render_template('messagebox.html', posts=posts) diff --git a/templates/advanced.html b/templates/advanced.html index de7f7d6..482b102 100755 --- a/templates/advanced.html +++ b/templates/advanced.html @@ -17,7 +17,7 @@ -
+
{% for elem in elementlist %} {% set row_i = loop %} @@ -25,7 +25,7 @@ -
+ @@ -33,7 +33,11 @@
-
+ + + + +
{{ elem }}
@@ -50,25 +54,73 @@
+ + + +
+
+
+
+ +
+ +
+
+ + +
+
+
+ + + + +
+
+
+ +
+
- +

Loading ...

+ + + + {% set col = 2 %} {% set col_size = 12//col %} - {% for name in paramlist %} + {% for name in paramlist %} {% set col_i = loop %} -
+