Remove socket.io dependency and use http server sent events instead

Signed-off-by: Thomas Schmid <tom@binary-kitchen.de>
This commit is contained in:
Thomas 2018-10-17 19:16:37 +02:00 committed by Thomas Schmid
parent f1c267dc94
commit ed239fb284
4 changed files with 58 additions and 44 deletions

View File

@ -16,7 +16,6 @@ depends=('python3'
'mosquitto' 'mosquitto'
'mpg123' 'mpg123'
'python-flask-wtf' 'python-flask-wtf'
'python-flask-socketio'
'python-paho-mqtt' 'python-paho-mqtt'
'chromium' 'chromium'
'xf86-video-fbdev' 'xf86-video-fbdev'

View File

@ -1,7 +1,7 @@
""" """
Doorlockd -- Binary Kitchen's smart door opener Doorlockd -- Binary Kitchen's smart door opener
Copyright (c) Binary Kitchen e.V., 2018 Copyright (c) Binary Kitchen e.V., 2020
Author: Author:
Ralf Ramsauer <ralf@binary-kitchen.de> Ralf Ramsauer <ralf@binary-kitchen.de>
@ -16,9 +16,10 @@ details.
""" """
import logging import logging
import json
import threading
from flask import abort, Flask, jsonify, render_template, request from flask import abort, Flask, jsonify, render_template, request, Response
from flask_socketio import SocketIO
from flask_wtf import FlaskForm from flask_wtf import FlaskForm
from wtforms import PasswordField, StringField, SubmitField from wtforms import PasswordField, StringField, SubmitField
from wtforms.validators import DataRequired, Length from wtforms.validators import DataRequired, Length
@ -28,19 +29,41 @@ from .Doorlock import DoorlockResponse
from .Authenticator import AuthMethod from .Authenticator import AuthMethod
log = logging.getLogger() log = logging.getLogger()
webapp = Flask(__name__) webapp = Flask(__name__)
socketio = SocketIO(webapp, async_mode='threading') evt = threading.Event()
json_push_state = ""
def emit_doorstate(response=None): def emit_doorstate(response=None):
state = logic.state global json_push_state
if response: if response:
message = str(response) message = str(response)
else: else:
message = str(state) message = str(logic.state)
socketio.emit('status', {'led': state.to_img(), 'message': message})
json_push_state = json.dumps({
'message' : message,
'img' : logic.state.to_img()})
#Notify push clients
evt.set()
evt.clear()
def event_str():
return "data: {}\n\n".format(json_push_state)
def push_state():
def event_str():
return "data: {}\n\n".format(json_push_state)
try:
yield event_str()
while True:
evt.wait()
yield event_str()
except GeneratorExit:
return
class AuthenticationForm(FlaskForm): class AuthenticationForm(FlaskForm):
username = StringField('Username', [Length(min=3, max=25)]) username = StringField('Username', [Length(min=3, max=25)])
@ -64,13 +87,6 @@ class AuthenticationForm(FlaskForm):
return True return True
@socketio.on('request_status')
@socketio.on('connect')
def on_connect():
emit_doorstate()
@webapp.route('/display') @webapp.route('/display')
def display(): def display():
return render_template('display.html', return render_template('display.html',
@ -78,19 +94,27 @@ def display():
title=title, title=title,
welcome=welcome) welcome=welcome)
@webapp.route('/push')
def push():
emit_doorstate()
resp = Response(push_state(),mimetype="text/event-stream")
resp.headers['X-Accel-Buffering'] = 'no'
resp.headers['Cache-Control'] = 'no-cache'
return resp
@webapp.route('/api', methods=['POST']) @webapp.route('/api', methods=['POST'])
def api(): def api():
def json_response(response, msg=None): def json_response(response, msg=None):
json = dict() json_dict = dict()
json['err'] = response.value json_dict['err'] = response.value
json['msg'] = str(response) if msg is None else msg json_dict['msg'] = str(response) if msg is None else msg
if response == DoorlockResponse.Success or \ if response == DoorlockResponse.Success or \
response == DoorlockResponse.AlreadyActive: response == DoorlockResponse.AlreadyActive:
# TBD: Remove 'open'. No more users. Still used in App Version 2.1.1! # TBD: Remove 'open'. No more users. Still used in App Version 2.1.1!
json['open'] = logic.state.is_open() json_dict['open'] = logic.state.is_open()
json['status'] = logic.state.value json_dict['status'] = logic.state.value
return jsonify(json) return jsonify(json_dict)
user = request.form.get('user') user = request.form.get('user')
password = request.form.get('pass') password = request.form.get('pass')
@ -137,6 +161,7 @@ def home():
desired_state = authentication_form.desired_state desired_state = authentication_form.desired_state
log.info(' desired state: %s' % desired_state) log.info(' desired state: %s' % desired_state)
log.info(' current state: %s' % logic.state) log.info(' current state: %s' % logic.state)
response = logic.request(desired_state, credentials) response = logic.request(desired_state, credentials)
log.info(' response: %s' % response) log.info(' response: %s' % response)
@ -154,7 +179,7 @@ def home():
def webapp_run(cfg, my_logic, status, version, template_folder, static_folder): def webapp_run(cfg, my_logic, status, version, template_folder, static_folder):
global logic global logic
logic = my_logic logic = my_logic
debug = cfg.boolean('DEBUG') debug = cfg.boolean('DEBUG')
host = 'localhost' host = 'localhost'
@ -176,4 +201,5 @@ def webapp_run(cfg, my_logic, status, version, template_folder, static_folder):
webapp.config['SECRET_KEY'] = cfg.str('SECRET_KEY') webapp.config['SECRET_KEY'] = cfg.str('SECRET_KEY')
webapp.template_folder = template_folder webapp.template_folder = template_folder
webapp.static_folder = static_folder webapp.static_folder = static_folder
socketio.run(webapp, host=host, port=8080, use_reloader=False, debug=debug) webapp.debug = debug
webapp.run(host = host, port = 8080)

File diff suppressed because one or more lines are too long

View File

@ -10,26 +10,18 @@
{{ super() }} {{ super() }}
<script src="/static/js/jquery.min.js"></script> <script src="/static/js/jquery.min.js"></script>
<script src="/static/js/bootstrap.min.js"></script> <script src="/static/js/bootstrap.min.js"></script>
<script type="text/javascript" src="static/socket.io.slim.js"></script>
<script type="text/javascript" charset="utf-8"> <script type="text/javascript" charset="utf-8">
$(document).ready(function() { $(document).ready(function() {
function request_status() { var source = new EventSource('{{ url_for('push')}}');
socket.emit('request_status'); source.onmessage = function(event){
} console.log("event:" + event.data);
try{
var socket = io.connect(location.protocol + '//' + document.domain + ':' + location.port); var data = JSON.parse(event.data);
var timer = setInterval(request_status, 120000); $('#message').html(data.message);
led = $('#led');
socket.on('connect', function() { led.attr('src', data.img);
socket.emit('connected'); } catch(e){console.log(e);}}
}); });
socket.on('status', function(status) {
$('#led').attr('src', status.led);
$('#message').html(status.message);
clearInterval(timer);
timer = setInterval(request_status, 120000);
});
});
</script> </script>
{% endblock %} {% endblock %}