/
app.py
executable file
·233 lines (199 loc) · 9.57 KB
/
app.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
#!/usr/bin/env python
from flask import Flask, render_template, session, request, url_for
from flask_socketio import SocketIO, Namespace, emit, join_room, leave_room, \
close_room, rooms, disconnect
from cloudinary.uploader import upload
from cloudinary.utils import cloudinary_url
import urllib2
import datetime
import threading
import base64
import socket
# Set this variable to "threading", "eventlet" or "gevent" to test the
# different async modes, or leave it set to None for the application to choose
# the best option based on installed packages.
async_mode = "eventlet"
page_title = "Control the Lights"
app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret!'
socketio = SocketIO(app, async_mode=async_mode)
background_image = None
background_arduino = None
class ArduinoManagement:
def __init__(self):
self.arduino_url = "http://192.168.86.168"
def callRestAPI(self, url_string):
success = False
try:
request = urllib2.urlopen(self.arduino_url + "/" + url_string, timeout=5)
response = request.read()
print "~~~ArduinoManagement~~~ rest api " + url_string + " response: " + response
success = True
except OSError as e:
print "~~~ArduinoManagement~~~: REST API failed: {}".format(str(e))
except Exception as e:
print "~~~ArduinoManagement~~~: REST API failed: {}".format(str(e))
return success
def powerOn(self):
return self.callRestAPI("powerOn")
def powerOff(self):
return self.callRestAPI("powerOff")
def arduino_manager_thread(threadArduinoManager, action):
print "---arduino thread---: Calling callRestAPI"
threadArduinoManager.callRestAPI(action);
def background_arduino_manager(**kwargs):
backgroundArduinoPowerOnEvent = kwargs["backgroundArduinoPowerOnEvent"]
backgroundArduinoPowerOffEvent = kwargs["backgroundArduinoPowerOffEvent"]
backgroundArduinoManager = kwargs["backgroundArduinoManager"]
backgroundUpdateEvent = kwargs["backgroundUpdateEvent"]
print "---background---: background_arduino_manager started"
while True:
# Wait time_to_wait_before_refresh seconds or until someone calls backgroundArduinoPowerOn/OffEvent.set()
action = ""
while not backgroundArduinoPowerOnEvent.is_set() and not backgroundArduinoPowerOffEvent.is_set():
socketio.sleep(1)
if backgroundArduinoPowerOnEvent.is_set():
backgroundArduinoPowerOnEvent.clear()
action = "powerOn"
elif backgroundArduinoPowerOffEvent.is_set():
backgroundArduinoPowerOffEvent.clear()
action = "powerOff"
print "---background---: handling arduino manager event"
# Handle event in a new thread, so the UI still responds
arduino_thread = threading.Thread(target=arduino_manager_thread, args = (backgroundArduinoManager, action, ))
arduino_thread.start()
while arduino_thread.is_alive():
socketio.sleep(1)
arduino_thread.join()
# Update client
socketio.emit('toggle_result',
{'status': "success",
'operation': action,
'status_msg': "Successfully turned " + ( "off" if action == "powerOff" else "on" ) + " light, image will auto-update"})
# And now signal the image to be updated
backgroundUpdateEvent.set()
class ImageUpdater:
def __init__(self):
self.last_image_url = "http://res.cloudinary.com/cloudmedia/image/upload/v1478798030/stream-unavailable.jpg"
self.last_update_time = "never"
self.camera_server = "http://192.168.86.113:8080"
self.filename = "images/image_latest.jpg"
self.last_refresh_attempt = datetime.datetime.min
self.refresh_interval = datetime.timedelta(seconds=5)
def refresh_image(self):
if self.last_refresh_attempt + self.refresh_interval > datetime.datetime.now():
print "+++ImageUpdater+++: Skipping this update"
# Return right away if we need to wait longer
return
self.last_refresh_attempt = datetime.datetime.now()
attempt_num = 0
max_attempts = 3
success = False
while attempt_num < max_attempts and success == False:
attempt_num += 1
try:
# Grab the latest frame from the camera
print "+++ImageUpdater+++: Opening request"
request = urllib2.urlopen(self.camera_server + "/photo.jpg", timeout=3)
print "+++ImageUpdater+++: Reading request"
data = request.read()
print "+++ImageUpdater+++: Encoding request"
encoded_str = base64.b64encode(data)
print "+++ImageUpdater+++: Closing request"
request.close()
success = True
except OSError as e:
print "+++ImageUpdater+++: Image read attempt {} failed: ".format(attempt_num) + str(e)
except Exception as e:
print "+++ImageUpdater+++: Image read attempt {} failed: ".format(attempt_num) + str(e)
if not success:
print "+++ImageUpdater+++: Failed to read new image"
else:
try:
# Upload the image to the cloud
print "+++ImageUpdater+++: Uploading request"
upload_result = upload("data:image/jpg;base64," + encoded_str)
print "+++ImageUpdater+++: upload returned"
if upload_result:
# If the upload was successful, save the result in last_image_url
self.last_image_url = upload_result['url']
self.last_update_time = datetime.datetime.now().strftime('%H:%M:%S')
except Exception as e:
print "+++ImageUpdater+++: Failed to upload image to cloud: " + str(e)
success = False
return success
def image_refresh_thread(threadImageUpdater):
print "---image thread---: Calling refresh_image"
threadImageUpdater.refresh_image()
def background_image_updater(**kwargs):
backgroundUpdateEvent = kwargs["backgroundUpdateEvent"]
backgroundImageUpdater = kwargs["backgroundImageUpdater"]
time_to_wait_before_refresh = 60
print "---background---: background_image_updater started"
while True:
# Wait time_to_wait_before_refresh seconds or until someone calls backgroundUpdateEvent.set()
time_waited = 0
while time_waited < time_to_wait_before_refresh and not backgroundUpdateEvent.is_set():
socketio.sleep(1)
time_waited += 1
print "---background---: refreshing image"
# Clear event right away, so if another set comes in we catch it
backgroundUpdateEvent.clear()
# Update the image in a new thread, so the UI still responds
image_thread = threading.Thread(target=image_refresh_thread, args = (backgroundImageUpdater, ))
image_thread.start()
while image_thread.is_alive():
socketio.sleep(1)
image_thread.join()
socketio.emit('picture_update',
{'source': backgroundImageUpdater.last_image_url, 'picture_msg': "Last update: " + backgroundImageUpdater.last_update_time},
namespace='')
@app.route('/')
def index():
return render_template('index.html', async_mode=socketio.async_mode, page_title=page_title)
class MyNamespace(Namespace):
def __init__(self, ns, nsImageUpdater, nsUpdateEvent, nsArduinoManager, nsArduinoPowerOnEvent, nsArduinoPowerOffEvent):
super(MyNamespace, self).__init__(ns)
self.imageUpdater = nsImageUpdater
self.updateEvent = nsUpdateEvent
self.arduinoManager = nsArduinoManager
self.arduinoPowerOnEvent = nsArduinoPowerOnEvent
self.arduinoPowerOffEvent = nsArduinoPowerOffEvent
def on_update_request(self):
self.updateEvent.set()
print "Update request received, signaled thread for update"
def on_turn_off_request(self):
print "Power Off request received, signaling thread"
self.arduinoPowerOffEvent.set()
def on_turn_on_request(self):
print "Power On request received, signaling thread"
self.arduinoPowerOnEvent.set()
def on_connect(self):
emit('picture_update',
{'source': self.imageUpdater.last_image_url,
'picture_msg': "Last update: " + self.imageUpdater.last_update_time})
global background_image
global background_arduino
if background_image is None:
print "Starting background_image"
background_image = socketio.start_background_task(
target=background_image_updater,
backgroundImageUpdater=self.imageUpdater,
backgroundUpdateEvent=self.updateEvent)
if background_arduino is None:
print "Starting background_arduino"
background_arduino = socketio.start_background_task(
target=background_arduino_manager,
backgroundArduinoManager=self.arduinoManager,
backgroundArduinoPowerOffEvent=self.arduinoPowerOffEvent,
backgroundArduinoPowerOnEvent=self.arduinoPowerOnEvent,
backgroundUpdateEvent=self.updateEvent)
self.updateEvent.set()
if __name__ == '__main__':
socketio.on_namespace(MyNamespace(ns='',
nsImageUpdater=ImageUpdater(),
nsUpdateEvent=threading.Event(),
nsArduinoManager=ArduinoManagement(),
nsArduinoPowerOnEvent=threading.Event(),
nsArduinoPowerOffEvent=threading.Event()))
socketio.run(app, debug=True, host="0.0.0.0", port=8080)