-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy patheggposter.py
193 lines (160 loc) · 8.54 KB
/
eggposter.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
import network
import _thread
import sim800l
import utime
# A file for variables like logins, urls, and wifi details for the T-Call
wifiSSID = "Add your ssid here"
wifipswd = "Your password in here"
postURL = "https://example.com/eggposter.php" # This is the URL of the eggs php file that the app will try and access
# ========== FarmOS Details ==========
# This is the "Chickens" asset you want to record the eggs to
assetJson = [{"uri": "https://url.to.the.asset", "id": "ID of the asset", "resource": "farm_asset"}]
egg_id = "43" # This is the id of the egg unit - you can get this from taxonomy_term.json?bundle=farm_quantity_units
farmOSAuth = "" # Unfortunately, micropython doesn't seem to have the base64 module, so you'll need to encode your username:password using a number of online tools
# Such as https://www.base64encode.org/ - eg: "restws_username:pass1234" would give you "cmVzdHdzX3VzZXJuYW1lOnBhc3MxMjM0" as your farmOSAuth
# TODO: Integrate this with micropython-farmOS.py, so we can use token authentication.
# ========== Messages ==========
# You can cusomise your responses here
received_text = "Received "
collected_text = "Collected "
egg_text = " egg"
eggs_text = " eggs"
thank_you_text = "\nThank you!"
error_message = "Your message wasn't understood\nSorry!"
# ========== Variables ==========
phone = None # This will be the phone object, when it has been initialised!
# ========== Functions ==========
# The main functions
def do_connect():
# do_connect will connect to the wifi you specified under egg_details
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
if not wlan.isconnected():
print("connecting")
wlan.connect(wifiSSID, wifipswd)
while not wlan.isconnected():
pass
print("network config:", wlan.ifconfig())
def initialisePhone():
global phone
phone = sim800l.Phone()
while True: # Check if the phone is online
reply = phone.sendCommand("AT") # Check to see if the module is active
if ("OK" in reply):
phone.sendCommand("AT+CMGF=1") # These commands are officially sent by the sim800 module, but sometimes it goofs, so we'll send them again to be safe!
phone.sendCommand("AT+CNMI=1,2,0,0,0")
break # And then let us continue!
else:
utime.sleep_ms(500) # If there is no reply, we'll wait a bit more
checkMessages(10) # Check the messages with a 10s delay between checking
def checkReturned(returnedString): # This takes an array from the message processer, and extracts the message and phone number
number = None
message = None
if(len(returnedString) == 2): # Just sanity check the data
commandString = returnedString[0] # The first bit should be the command string, starting with CMT
commandString = commandString.lower() # Just make it lower case.. makes everyones life easier if it is all the same
# Test if it is actually a CMT message:
if "cmt" in commandString:
number = commandString.split(",")[0]
# the response is usually '+CMT: "+441234567890","","19/08/16,15:15:52+04"\r\n4 eggs\r\n
# So we split it at the , which gives us +CMT: "+441234567890". Next, split it at the "
number = number.split('"')[1] # And take the 2nd half, the phone number!
# It is a text message, so lets get the actual message:
message = returnedString[1].lower() # Second bit of the input string should be the message!
# Make it lower to rule out people writing "EgGs"...You know what people are like...
return [number, message] # Either None, or the actual thing!
def quantityFromMessage(message):
if ("egg" in message):
product = "eggs"
count = getNumber(message)
return [int(count), product]
return None
def getNumber(inputString):
"""
The getnumber function will return a float, so if you pass "bob 12.345", you will have
12.345 back. This will work (potentiall) in the future with other quantities like milk yields, where
decimal places are needed. For eggs, this float will be converted back to an int, so you're not adding 1.2 eggs!
"""
numParse = ""
for i in range(len(inputString)): # Iterate through the string, but with range, we iterate through the indexes
char = inputString[i]
if (char.isdigit() or char == '.'):
numParse += char
try:
nexti = inputString[i+1]
except IndexError: # This is called if the numbers are at the end of the string.
return(float(numParse))
break
if not (nexti.isdigit() or nexti == "."): # This on is called if the number is at the beginning of the string
return(float(numParse))
break
def checkMessages(delay):
if (type(phone) is not None): # If the phone has been initialised
_thread.start_new_thread(getLatest, [delay])
else:
print("Phone has not been initialised")
def getLatest(delay):
# This function will check the latest messages on the SIM every $delay seconds
while True:
utime.sleep_ms(delay * 1000)
response = phone.readAll() # Get all the messages from the phone
if (response is not None): # If there is actually a response on the SIM....
responseStr = response.decode('utf-8') # Decode it into utf-8
splitString = responseStr.splitlines() # Split it into a string array, based on CR/NLs - May need to
# check if this still works if someone writes:
# Hello phone! \r\n
# I'd like to add 3 eggs please
# Might not work as the message has a CR/NL....
for i in splitString:
if ("CMT" in i): # Is one of the responses a message response (could also be anything, AT replies, all ready replies etc)
index = splitString.index(i) # And where is that message in the array?
message = []
message.append(splitString[index]) # Add the command + phone number
message.append(splitString[index+1]) # Add the message (command +1)
[number, text] = checkReturned(message) # Get the phone number and the message from the array
quantity = quantityFromMessage(text)[0] # Get the int from the message
if ((quantity is not None) and (quantity > 0)): # Check if it has worked...
reply = received_text + str(quantity) + eggs_text + thank_you_text
json = createHarvestJSON(quantity)
if json is not None:
success = postJSON(json)
if phone is not None:
if not success:
phone.sendText(number, "Sorry, the record couldn't be sent, try again, or ask the administrator")
else:
phone.sendText(number, reply)
else:
print("How did we get the message with no phone?!")
else:
if phone is not None:
phone.sendText(number, error_message)
def createHarvestJSON(count):
json = {}
if (type(count) is int):
if (count > 1): # more than 1 egg
json["name"] = collected_text + str(count) + eggs_text
else:
json["name"] = collected_text + str(count) + egg_text # Singular egg... Lonely. And who only get 1 egg per day?!
json["type"] = "farm_harvest"
json["done"] = "1"
json["asset"] = assetJson
json["quantity"] = [{"measure": "count", "value": str(count), "unit": {"id": egg_id}}]
return json
else:
return None
def postJSON(j, retries=0):
if retries < 6: # Only retry the connection 5 times, otherwise, give up TODO: Don't give up silently!
import urequests as requests
wlan = network.WLAN(network.STA_IF)
if wlan.isconnected(): # Check connection
r = requests.post(url=postURL, json=j, headers={"Authorization": "Basic " + farmOSAuth}) # Send the psot request
if (r.status_code == 201): # Check if the response is 201: Created - The correct reponse from FarmOS
print("success")
return True
else: # Otherwise, retry
postJSON(j, retries+1)
else:
do_connect()
postJSON(j, retries+1)
else:
return False