forked from mdegrazia/piWarmer
-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathfona_manager.py
307 lines (244 loc) · 9.28 KB
/
fona_manager.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
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
"""
Module to abstract handling and updating
the Fona in a thread safe way.
"""
import sys
import threading
import time
from multiprocessing import Queue as MPQueue
import text
import lib.local_debug as local_debug
import lib.fona as fona
from lib.recurring_task import RecurringTask
class FonaManager(object):
"""
Object to handle the Fona board and abstract
away all the upkeep tasks associated with the board.
Keeps message sending & reception on a single thread.
Handles updating the signal strength, battery state,
and other monitoring of the device.
"""
CHECK_SIGNAL_INTERVAL = 60 # Once a minute
CHECK_BATTERY_INTERVAL = 60 * 5 # Every five minutes
DEFAULT_RETRY_ATTEMPTS = 4
def is_power_on(self):
"""
Is the Fona on?
"""
return self.__fona__.is_power_on()
def update(self):
"""
Performs all of the processing of receiving,
sending, and status....
... on a single thread...
"""
self.__process_status_updates__()
self.__process_send_messages__()
def send_message(self,
phone_number,
text_message,
maximum_number_of_retries=DEFAULT_RETRY_ATTEMPTS):
"""
Queues the message to be sent out.
"""
self.__lock__.acquire(True)
self.__send_message_queue__.put(
[phone_number, text_message, maximum_number_of_retries])
self.__lock__.release()
def signal_strength(self):
"""
Handles returning a cell signal status
in a thread friendly manner.
"""
return self.__current_signal_strength__
def battery_condition(self):
"""
Handles returning the battery status
in a thread friendly manner.
"""
return self.__current_battery_state__
def is_message_waiting(self):
"""
Is there a message waiting for us to unpack?
"""
return self.__fona__.is_message_waiting()
def get_messages(self):
"""
Gets any messages from the Fona.
"""
results = []
self.__lock__.acquire(True)
try:
results = self.__fona__.get_messages()
except:
exception_message = "ERROR fetching messages!"
print exception_message
self.__logger__.log_warning_message(exception_message)
self.__lock__.release()
return results
def delete_messages(self):
"""
Deletes any messages from the Fona.
"""
num_deleted = 0
self.__lock__.acquire(True)
try:
num_deleted = self.__fona__.delete_messages()
except:
exception_message = "ERROR deleting messages!"
print exception_message
self.__logger__.log_warning_message(exception_message)
self.__lock__.release()
return num_deleted
def delete_message(self, message_to_delete):
"""
Deletes any messages from the Fona.
"""
self.__lock__.acquire(True)
try:
self.__fona__.delete_message(message_to_delete)
except:
exception_message = "ERROR deleting message!"
print exception_message
self.__logger__.log_warning_message(exception_message)
self.__lock__.release()
def __update_battery_state__(self):
"""
Updates the battery state.
"""
self.__current_battery_state__ = self.__fona__.get_current_battery_condition()
def __update_signal_strength__(self):
"""
Updates the battery state.
"""
self.__current_signal_strength__ = self.__fona__.get_signal_strength()
def __process_status_updates__(self):
"""
Handles updating the cell signal
and battery status.
"""
# Only perform these checks once per
# update. This lets us clear the thread
# faster and prevents redundant work.
battery_checked = False
signal_checked = False
self.__lock__.acquire(True)
try:
while not self.__update_status_queue__.empty():
command = self.__update_status_queue__.get()
if text.CHECK_BATTERY in command and not battery_checked:
self.__update_battery_state__()
battery_checked = True
if text.CHECK_SIGNAL in command and not signal_checked:
self.__update_signal_strength__()
signal_checked = True
except:
exception_message = "ERROR updating signal & battery status!"
print exception_message
self.__logger__.log_warning_message(exception_message)
self.__lock__.release()
def __process_send_messages__(self):
"""
Handles sending any pending messages.
"""
messages_to_retry = []
self.__lock__.acquire(True)
try:
while not self.__send_message_queue__.empty():
self.__logger__.log_info_message("__send_message_queue__")
message_to_send = self.__send_message_queue__.get()
self.__logger__.log_info_message(
"done: __send_message_queue__")
try:
self.__logger__.log_info_message("sending..")
self.__fona__.send_message(
message_to_send[0], message_to_send[1])
self.__logger__.log_info_message("done sending")
except:
self.__logger__.log_warning_message(
"Exception servicing outgoing message:" + str(sys.exc_info()[0]))
message_to_send[3] -= 1
if message_to_send[3] > 0:
messages_to_retry.append(message_to_send)
except:
self.__logger__.log_warning_message(
"Exception servicing outgoing queue:" + str(sys.exc_info()[0]))
for message_to_retry in messages_to_retry:
self.__logger__.log_warning_message(
"Adding message back for up to" + str(message_to_retry[3]) + " more retries.")
self.__send_message_queue__.put(message_to_retry)
self.__lock__.release()
def __trigger_check_battery__(self):
"""
Triggers the battery state to be checked.
"""
self.__update_status_queue__.put(text.CHECK_BATTERY)
def __trigger_check_signal__(self):
"""
Triggers the signal to be checked.
"""
self.__update_status_queue__.put(text.CHECK_SIGNAL)
def __init__(self,
logger,
serial_connection,
power_status_pin,
ring_indicator_pin,
utc_offset):
"""
Initializes the Fona.
"""
fona.TIMEZONE_OFFSET = utc_offset
self.__logger__ = logger
self.__lock__ = threading.Lock()
self.__fona__ = fona.Fona(logger,
serial_connection,
power_status_pin,
ring_indicator_pin)
self.__current_battery_state__ = None
self.__current_signal_strength__ = None
self.__update_status_queue__ = MPQueue()
self.__send_message_queue__ = MPQueue()
# Update the status now as we dont
# know how long it will be until
# the queues are serviced.
self.__update_battery_state__()
self.__update_signal_strength__()
RecurringTask("check_battery",
self.CHECK_BATTERY_INTERVAL,
self.__trigger_check_battery__,
self.__logger__)
RecurringTask("check_signal",
self.CHECK_SIGNAL_INTERVAL,
self.__trigger_check_signal__,
self.__logger__)
if __name__ == '__main__':
import serial
PHONE_NUMBER = "2061234567"
if local_debug.is_debug():
SERIAL_CONNECTION = None
else:
SERIAL_CONNECTION = serial.Serial('/dev/ttyUSB0', 9600)
FONA_MANAGER = FonaManager(None,
SERIAL_CONNECTION,
fona.DEFAULT_POWER_STATUS_PIN,
fona.DEFAULT_RING_INDICATOR_PIN,
fona.TIMEZONE_OFFSET)
if not FONA_MANAGER.is_power_on():
print "Power is off.."
exit()
BATTERY_CONDITION = FONA_MANAGER.battery_condition()
FONA_MANAGER.send_message(PHONE_NUMBER,
"Time:" + str(time.time()) + "\nPCT:"
+ str(BATTERY_CONDITION.battery_percent)
+ "\nv:" + str(BATTERY_CONDITION.battery_voltage))
SIGNAL_STRENGTH = FONA_MANAGER.signal_strength()
print "Signal:" + SIGNAL_STRENGTH.classify_strength()
while True:
BATTERY_CONDITION = FONA_MANAGER.battery_condition()
SIGNAL_STRENGTH = FONA_MANAGER.signal_strength()
if FONA_MANAGER.is_message_waiting():
MESSAGES = FONA_MANAGER.get_messages()
FONA_MANAGER.delete_messages()
print "Battery:" + str(BATTERY_CONDITION.battery_percent)
print "Signal:" + SIGNAL_STRENGTH.classify_strength()
FONA_MANAGER.update()