I am using RPI, VPS, and socket io. I want to create a website where a user can go to click a button and get a pic from the pi.
I wrote the server and the client applications in python.
the server uses socketio + flask
server.py
from flask import Flask, request, render_template
from flask_socketio import SocketIO, rooms, join_room, leave_room
app = Flask(__name__, static_url_path='/static')
app.config['SECRET_KEY'] = 'secret!'
sio = SocketIO(app)
#app.route('/')
def index():
"""Serve the client-side application."""
with open('index.html') as f:
return f.read()
# return app.send_static_file('index.html')
#sio.on('connect')
def connect():
print('Connected:')
#sio.on('join')
def on_join(room):
join_room(room)
print(request.sid + ' joined room ' + room )
#sio.on('leave')
def on_leave(room):
leave_room(room)
print(request.sid + ' left room ' + room )
#sio.on('message')
def handle_json(message):
# print('Received json: ')
# print(message)
room = rooms(request.sid)[0]
print('Forwarding to room:', room)
sio.send(message, room=room, skip_sid=request.sid, json=True)
if __name__ == '__main__':
sio.run(app, host= "142.11.210.25", port = 80)
rpi_client.py
import io
import time
import picamera
import socketio
import base64
sio = socketio.Client()
# Specify the room
room = 'cam_1'
socket_url = 'http://142.11.210.25:80/'
def capture_b64_image():
# Create an in-memory stream
image_stream = io.BytesIO()
# Capture image
with picamera.PiCamera() as camera:
# Camera warm-up time
time.sleep(2)
camera.capture(image_stream, 'jpeg')
# Encode the image
image_bytes = image_stream.getvalue()
return base64.b64encode(image_bytes).decode()
#sio.on('connect')
def on_connect():
print('Connection established')
sio.emit('join', room)
#sio.on('json')
def on_message(data):
print('Received message:', data)
encoded_image = capture_b64_image()
print( len(encoded_image) )
sio.send({'image': encoded_image})
#sio.on('disconnect')
def on_disconnect():
print('Disconnected from server')
sio.connect(socket_url)
sio.wait()
index.html
<!DOCTYPE html>
<html>
<head>
<title>SocketIO Demo</title>
</head>
<body>
<img id="image-preview" src="" />
<button id='cam_click'>Take photo</button>
<script
src="http://code.jquery.com/jquery-3.3.1.js"
integrity="sha256-2Kok7MbOyxpgUVvAk/HJ2jigOSYS2auK4Pfzbm7uH60="
crossorigin="anonymous"></script>
<script src="/static/js/socket.io.js"></script>
<script>
var socket = io('/');
var room = 'cam_1';
function bytes2ascii(bytes) {
var str = '';
for(var i = 0; i < bytes.length; i++) {
str += String.fromCharCode(bytes[i]);
}
return str;
}
socket.on('connect', function() {
console.log(11);
socket.emit('join', room);
});
socket.on('json', function (data) {
console.log('Received:');
console.log(data);
// $('#cam_content').html(data.image);
//var encoded_image = bytes2ascii(new Uint8Array(data.image) );
var encoded_image = data.image
$('#image-preview').attr('src', `data:image/png;base64,${encoded_image}`);
});
$(document).ready(function() {
console.log('Ready...');
$('#cam_click').click(function() {
socket.send('image');
});
});
</script>
</body>
</html>
when i run the server and the rpi client, I get connection established, and when I click on the button to take photo in the index.html, the server fowrards to room1 and I get that on the rpi client and it takes a pic, but then it crashes when its sending the pic and it gives me
TypeError: emit() got an unexpected keyword argument 'wait'
here is the error I get(rpi client) when I run the codes.
Connection established
Received message: image
996008
Exception in thread Thread-5:
Traceback (most recent call last):
File "/usr/lib/python3.5/threading.py", line 914, in _bootstrap_inner
self.run()
File "/usr/lib/python3.5/threading.py", line 862, in run
self._target(*self._args, **self._kwargs)
File "/usr/local/lib/python3.5/dist-packages/socketio/client.py", line 514, in _handle_eio_message
self._handle_event(pkt.namespace, pkt.id, pkt.data)
File "/usr/local/lib/python3.5/dist-packages/socketio/client.py", line 413, in _handle_event
r = self._trigger_event(data[0], namespace, *data[1:])
File "/usr/local/lib/python3.5/dist-packages/socketio/client.py", line 455, in _trigger_event
return self.handlers[namespace][event](*args)
File "rpi_client2.py", line 41, in on_message
sio.send({'image': encoded_image})
File "/usr/local/lib/python3.5/dist-packages/socketio/client.py", line 296, in send
callback=callback, wait=wait, timeout=timeout)
TypeError: emit() got an unexpected keyword argument 'wait'
I installed python-socketio[client] as instructed.
What could be causing the error and whats the workaround? thank you and have a nice day!
According to the python-socketio doc, https://python-socketio.readthedocs.io/en/latest/client.html#emitting-events:
For convenience, a send() method is also provided. This method accepts a data element as its only argument, and emits the standard message event with it:
sio.send('some data')
Therefore, you can change:
sio.send({'image': encoded_image})
to:
sio.emit('message', {'image': encoded_image})
Related
I am doing a Flask tutorial for a Restful API and am trying to make the stores variable appear as an alert on the homepage using the following syntax in both files.
Any ideas on why I can't seem to get the alert to pop up in the browser whenever I go to localhost ?
Additionally, I have been told by Stack Overflow that my post does not contain enough words so I am typing more words here.
main.py file
from flask import Flask, jsonify, request, render_template
app = Flask(__name__)
stores =[
{
'name':'The Best Store',
'items':[
{
'name': 'Air Jordans',
'price': 16.99
}
]
}
]
#simulating an online store
#app.route('/')
def home():
return render_template('index.html')
#app.route('/store', methods=['POST'])
def create_store():
request_data = request.get_json() #function for handling request from browser to add post request
new_store = {
'name': request_data['name'],
'items': []
}
stores.append(new_store)
return jsonify(new_store)
#app.route('/store/<string:name>')
def get_store(name): #function to check through existing store names and print error if already found in DB ß
for store in stores:
if store['name'] == name:
return jsonify(store)
return("store not found")
#app.route('/store')
def get_stores():
return jsonify({'stores':stores}) #the stores variable above is a list. We need to make iti nto a dictionary for jsonify to turn into a Json.
#app.route('/store/<string:name>/item', methods=['POST'])
def create_item_in_store(name): #check to see if the post method is equal to a store and the append the new names / prices
for store in stores:
request_data = request.get_json()
if store['name'] == name:
new_item = {
'name': request_data['name'],
'price': request_data['price']
}
store['items'].append(new_item)
return jsonify(new_item)
return jsonify({'message': 'store not found'}) #make sure this is outside of the loop ... obvious reasons
#app.route('/store/<string:name>/item')
def get_items_in_store(name):
for store in stores:
if store['name'] == name:
return jsonify({'items': store['items']})
return jsonify({'message': 'store not found'})
app.run(port=5000)
app.debug=True
index.html file
<html>
<head>
<script type="text/javascript">
function httpGetAsync(theUrl, callback) {
var xmlHttp = new XMLHttpRequest();
xmlHttp.onreadystatechange = function() {
if (xmlHttp.readyState == 4 && xmlHttp.status == 200)
callback(xmlHttp.responseText);
}
xmlHttp.open("GET", theUrl, true); // true for asynchronous
xmlHttp.send(null);
}
httpGetAsync('http://127.0.0.1:5000/store', function() {
alert(response);
})
</script>
</head>
<body>
<div id="myElement">
Hello, world!
</div>
</body>
</html>
There are many tutorials available for Flask and SocketIO, I could not find any for a simple threaded approach that I understood. But I did read and followed many of them.
I'd like to show my Python application on a web page using websockets so it's kind-of real-time monitoring. This is me trying to understand how to implement this.
The code I currently have is working for except the emit part. There does not seem to be any transfer of data. I'd like to know why.
The socket.on('tingeling' ... is not being triggered.
My Python code, mostly taken from https://codeburst.io/building-your-first-chat-application-using-flask-in-7-minutes-f98de4adfa5d
from flask import Flask, render_template
from flask_socketio import SocketIO
app = Flask(__name__)
app.config['SECRET_KEY'] = ''
socketio = SocketIO(app)
thread = None
def counter():
print("counter ding")
counting = 0
while True:
counting += 1
socketio.sleep(2)
socketio.emit('tingeling', counting, namespace='')
print(f"Counter: {counting}")
#app.route('/')
def sessions():
print('route')
return render_template('index.html')
#socketio.on('my event')
def connected(data):
print('my event')
#socketio.on('connect')
def starten():
print("connect")
socketio.emit('tingeling', 'start')
global thread
if thread is None:
print('thread ding')
thread = socketio.start_background_task(target=counter())
return render_template('index.html')
if __name__ == '__main__':
socketio.run(app, debug=True)
And my HTML template:
<!DOCTYPE html>
<html lang="en">
<head>
<title>fristi</title>
</head>
<body>
<h3 style='color: #ccc;font-size: 30px;'>waiting</h3>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/1.7.3/socket.io.min.js"></script>
<script type="text/javascript">
var socket = io.connect('http://' + document.domain + ':' + location.port);
console.log('doet iets')
socket.on( 'connect', function() {
socket.emit( 'my event', {
data: 'User Connected'
})
})
socket.on('tingeling', function(msg) {
console.log('iets komt binnen')
console.log(msg)
})
</script>
</body>
</html>
My error is on the line: thread = socketio.start_background_task(target=counter())
There I reference the function to run as a background task but I use the notation with () and that is not allowed because is runs the function and does not provide the start_background_task with a reference to this function.
I have the following Python script which is using Flask-socketio
from flask import Flask, render_template
from flask_socketio import SocketIO, emit
from time import sleep
app = Flask(__name__)
app.config['SECRET_KEY'] = 'P#ssw0rd'
socketio = SocketIO(app)
#app.route('/')
def index():
return render_template('index.html')
#socketio.on('connect')
def on_connect():
payload1 = 'Connected!!!'
payload2 = 'Doing thing 1'
payload3 = 'Doing thing 2'
emit('send_thing', payload1, broadcast=True)
sleep(2)
emit('send_thing', payload2, broadcast=True)
sleep(2)
emit('send_thing', payload3, broadcast=True)
if __name__ == '__main__':
socketio.run(app)
And here is the corresponding index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>SocketIO Python</title>
</head>
<body>
<div id="my-div"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/1.4.5/socket.io.js"></script>
<script>
(function init() {
var socket = io()
var divElement = document.getElementById('my-div')
socket.on('send_thing', function(payload) {
var dataElement = document.createElement('inner')
dataElement.innerHTML = payload
divElement.appendChild(dataElement)
})
})()
</script>
</body>
</html>
What I am trying to achieve is that when a client connects, it first says 'Connected!!!' and then 2 seconds later a new 'inner' element appears that says 'Doing thing 1' followed by 2 seconds later a new 'inner' element appears that says 'Doing thing 2' etc.
But what is happening is that when a client connects, it sends all 3 lines at the same time (after 4 seconds which is both sleep statements). This is the first time using SocketIO so I'm sure I've done something wrong.
When you use eventlet or gevent, the time.sleep() function is blocking, it does not allow any other tasks to run.
Three ways to address this problem:
Use socketio.sleep() instead of time.sleep().
Use eventlet.sleep() or gevent.sleep().
Monkey patch the Python standard library so that time.sleep() becomes async-friendly.
Setup: I'm trying to create a simple D3 js demo with Python 3 handling requests. Following the steps below I expected to see an error or some kind of HTML generated when I click on my html page with my d3 method. Instead, I receive a blank page.
1) Create custom HTTPRequestHandler class to handle GET requests
from http.server import BaseHTTPRequestHandler, HTTPServer
import os
class HTTPRequestHandler(BaseHTTPRequestHandler):
#handle GET command
def do_GET(self):
rootdir = r'C:\Current_Working_Directory' #file location
try:
if self.path.endswith('.json'):
f = open(rootdir + self.path, 'rb') #open requested file
#send code 200 response
self.send_response(200)
#send header first
self.send_header('Content-type','applicaon/json')
self.end_headers()
#send file content to client
self.wfile.write(f.read())
f.close()
return
except IOError:
self.send_error(404, 'file not found')
def run():
print('http server is starting...')
#ip and port of server
server_address = ('127.0.0.1', 80)
httpd = HTTPServer(server_address, HTTPRequestHandler)
print('http server is running...')
httpd.serve_forever()
if __name__ == '__main__':
run()
2) Save above file as server.py and run python server.py and create some dummy json data stored in data.json in $rootdir and create d3.html:
<!DOCTYPE html>
<html>
<head>
<title>D3 tutorial 10</title>
<script src=”http://d3js.org/d3.v3.min.js”></script>
</head>
<body>
<script>
d3.json(“dummy.json”, function (error,data) {
if (error) return console.warn(error);
console.log(data)
});
</script>
</body>
</html>
4) Open up web page expecting to see an error or some indication that d3 has sent the GET request to the server, but get a blank page.
Looking for some code samples to solve this problem :-
Would like to write some code (Python or Javascript) that would act as a subscriber to a RabbitMQ queue so that on receiving a message it would broadcast the message via websockets to any connected client.
I've looked at Autobahn and node.js (using "amqp" and "ws" ) but cannot get things to work as needed. Here's the server code in javascript using node.js:-
var amqp = require('amqp');
var WebSocketServer = require('ws').Server
var connection = amqp.createConnection({host: 'localhost'});
var wss = new WebSocketServer({port:8000});
wss.on('connection',function(ws){
ws.on('open', function() {
console.log('connected');
ws.send(Date.now().toString());
});
ws.on('message',function(message){
console.log('Received: %s',message);
ws.send(Date.now().toString());
});
});
connection.on('ready', function(){
connection.queue('MYQUEUE', {durable:true,autoDelete:false},function(queue){
console.log(' [*] Waiting for messages. To exit press CTRL+C')
queue.subscribe(function(msg){
console.log(" [x] Received from MYQUEUE %s",msg.data.toString('utf-8'));
payload = msg.data.toString('utf-8');
// HOW DOES THIS NOW GET SENT VIA WEBSOCKETS ??
});
});
});
Using this code, I can successfully subscribe to a queue in Rabbit and receive any messages that are sent to the queue. Similarly, I can connect a websocket client (e.g. a browser) to the server and send/receive messages. BUT ... how can I send the payload of the Rabbit queue message as a websocket message at the point indicated ("HOW DOES THIS NOW GET SENT VIA WEBSOCKETS") ? I think it's something to do with being stuck in the wrong callback or they need to be nested somehow ...?
Alternatively, if this can be done easier in Python (via Autobahn and pika) that would be great.
Thanks !
One way to implement your system is use python with tornado.
Here the server:
import tornado.ioloop
import tornado.web
import tornado.websocket
import os
import pika
from threading import Thread
clients = []
def threaded_rmq():
connection = pika.BlockingConnection(pika.ConnectionParameters("localhost"));
print 'Connected:localhost'
channel = connection.channel()
channel.queue_declare(queue="my_queue")
print 'Consumer ready, on my_queue'
channel.basic_consume(consumer_callback, queue="my_queue", no_ack=True)
channel.start_consuming()
def consumer_callback(ch, method, properties, body):
print " [x] Received %r" % (body,)
for itm in clients:
itm.write_message(body)
class SocketHandler(tornado.websocket.WebSocketHandler):
def open(self):
print "WebSocket opened"
clients.append(self)
def on_message(self, message):
self.write_message(u"You said: " + message)
def on_close(self):
print "WebSocket closed"
clients.remove(self)
class MainHandler(tornado.web.RequestHandler):
def get(self):
print "get page"
self.render("websocket.html")
application = tornado.web.Application([
(r'/ws', SocketHandler),
(r"/", MainHandler),
])
if __name__ == "__main__":
thread = Thread(target = threaded_rmq)
thread.start()
application.listen(8889)
tornado.ioloop.IOLoop.instance().start()
and here the html page:
<html>
<head>
<script src="//code.jquery.com/jquery-1.11.0.min.js"></script>
<script>
$(document).ready(function() {
var ws;
if ('WebSocket' in window) {
ws = new WebSocket('ws://localhost:8889/ws');
}
else if ('MozWebSocket' in window) {
ws = new MozWebSocket('ws://localhost:8889/ws');
}
else {
alert("<tr><td> your browser doesn't support web socket </td></tr>");
return;
}
ws.onopen = function(evt) { alert("Connection open ...")};
ws.onmessage = function(evt){
alert(evt.data);
};
function closeConnect(){
ws.close();
}
});
</script>
</head>
<html>
So when you publish a message to "my_queue" the message is redirects to all web page connected.
I hope it can be useful
EDIT**
Here https://github.com/Gsantomaggio/rabbitmqexample you can find the complete example