I am trying to make an Intranet of Things (IoT) page. I am using Flask for the python backend. I am trying to update the state of my light in real time. I believe the route to go is the AJAX/XMLHttpRequest on the front end. I am struggling to figure out how to send the JSON to the front end from the back end. Every example online seems to be a POST request instead of Get.
I believe I have the object created correctly as well as open and sending it correctly. I think my error is on the python side and not doing request right. I tried copying this example https://gist.github.com/KentaYamada/2eed4af1f6b2adac5cc7c9063acf8720. All the other stack overflow examples seem to do different methods like return or add an if statement for the return. I am not sure example how to setup to return the 'get' argument in request.args.get[]. The suggested potential duplicates only show one end, are in express js with the $ syntax which I'm not familiar with, or something similar
I am currently getting 500 errors. Below is my code.
HTML:
<html> <head>
<meta name = "Viewport" context = "width=device-width. initial-scale=1.0">
<title>Chang's IoT Controls</title>
<link href="{{url_for('static',filename='Styles/Controls.css')}}" rel="stylesheet" type="text/css"/>
</head>
<body>
<h1>Controls</h1>
<p> Controls to control Chang's IoT Hub</p>
<figure class="item">
<img src="{{light}}" id="light" onclick="Power(this)" />
<figcaption id = light1 class="caption">Light</figcaption>
</figure>
<figure class="item">
<img src="{{url_for('static',filename='Images/OffLight.jpg')}}" id="temp" onclick="Power(this)" />
<figcaption class="Temperature" id="state" <span style="border:2px solid black">Here</span></figcaption>
<button type = "button" id="toggle" onclick ="Power(this)" > Change Lights </button>
</figure>
<figure class="item">
<img src="{{temp}}" id="temp" onclick="Power(this)" />
<figcaption id = temperature class="caption">{{degree}}</figcaption>
</figure>
<script>
document.getElementById("state").innerHTML = "reached";
const xhttp = new XMLHttpRequest();
xhttp.onload = function () {
const xmlDoc = JSON.parse(this.responseText);
const status = xmlDoc.Status;
document.getElementById("state").innerHTML = status;
if(status == "On")
{
document.getElementById("temp").src = "{{url_for('static',filename='Images/OnLight.jpg')}}";
}
if(status == "Off")
{
document.getElementById("temp").src = "{{url_for('static',filename='Images/OffLight.jpg')}}";
}
}
xhttp.open("GET",'/get_json');
xhttp.send();
console.log(xhttp.response);
</script>
<p>
Homepage
</p>
</body>
</html>
Python:
from flask import Flask, redirect, url_for, render_template, request
app = Flask(__name__)
#app.route("/", methods=["POST", "GET"])
def home():
imgname = url_for('static', filename='Images/OnLight.jpg')
thermo = url_for('static', filename='Images/Thermometer.png')
heat = 50
# if request.method == "GET":
# file = request.form(url_for('static',filename='test.json'))
# return file
return render_template("Controls.html", light=imgname, temp=thermo, degree=heat)
#app.route('/get_json')
def get_json():
return jsonify(Status= "On")
if __name__=="__main__":
app.run(host="0.0.0.0", port=80)
JSON:
{
"Light":
[{"Status": "On"}]
}
I tried looking at examples including git repos like https://gist.github.com/KentaYamada/2eed4af1f6b2adac5cc7c9063acf8720 as well as stack overflow pages like Getting data from xhr request using python and how to properly get data from javascript to my python flask backend using XMLHttpRequest? as well as looked at the APIs
You can create different routes for html and for json.
For your Json try something like below
#app.route(“/get_json“, methods = [“GET”])
def get_json_to_frontend():
// logic
return jsonify( your response)
for html you can use your home() function that you’ve already created
Assume your response is :
{ "state" : "Ok" }
This is what you're Javascript should look like:
let xhttp = new XMLHttpRequest();
xhttp.onload = () => {
let t = JSON.parse(xhttp.response);
console.log(t.state);
}
xhttp.open("GET", URL);
xhttp.send();
This should work. It's pretty straight forward.
Related
I'm trying to load an image file with flask, and then update the same file with a different image and display the newly updated image without having to refresh the page.
I've tried different combinations with ajax and jquery and none have worked so far but it is possible I'm just missing a really simple fix.
This is the index.html file I currently have
<script src ="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<body>
<div>
<img id="my_image" src = "{{img}}">
</div>
<script>
function getimg(){
$.ajax({
type: 'GET',
cache: false,
url: "{{url_for('get_img')}}",
success: function(resp){
dt = new Date();
$('my_image').attr('src', resp.img + "?" + dt.getTime());
}
});
}
$(document).ready(function(){
setInterval(getimg, 3000);
});
</script>
</body>
And this is the flask routes from getImage.py
import json
from flask import render_template
#app.route('/img')
def open_image():
img = 'static\\testing.jpg'
return render_template('index.html',**locals())
#app.route('/get_img', methods = ['GET'])
def get_img():
img = 'static\\testing.jpg'
return json.dumps({img:img})
I know the ajax is sending requests through to flask via cmd but it does not actually update the image.
I have a simple Flask app like so:
import msgpack
from flask import Flask, render_template
app = Flask(__name__)
#app.route('/')
def index():
return render_template('index.html')
#app.route('/test')
def test():
return msgpack.packb([1, 2, 3])
#app.route('/ping')
def ping():
return "Pong"
if __name__ == '__main__':
app.run(debug=True, port=5000)
In the Flask route /test, I'm using msgpack to pack an array which is returned as a response. When this response is printed to the console, it looks something like this: b'\x93\x01\x02\x03'
I'm using AngularJS 1.7 in my front end which looks like:
<!doctype html>
<html ng-app="MyApp">
<body ng-cloak>
<div class="container" ng-controller="MyController">
<h1>Hello, {{text}}</h1>
<button class="btn btn-primary" ng-click="ping()">Ping</button>
<button class="btn btn-primary" ng-click="getMsgpackData()">Get Msgpack Data</button>
</div>
<script src="jquery-3.3.1.slim.min.js"></script>
<script src="/angular.js/1.7.2/angular.min.js"></script>
<script src="https://rawgithub.com/kawanet/msgpack-lite/master/dist/msgpack.min.js"></script>
<script>
var myApp = angular.module("MyApp", []);
myApp.controller("MyController", ["$scope", "$http", function($scope, $http) {
$scope.text = "World";
$scope.ping = function() {
$http.get("/ping").then(function success(response) {
$scope.text = response.data;
}, function error(response) {
console.log(response);
});
}
$scope.getMsgpackData = function() {
$http.get("/test").then(function success(response) {
console.log(response);
$scope.text = msgpack.decode(response.data);
}, function error(response) {
console.log(response);
});
}
}]);
</script>
</body>
</html>
When the getMsgpackData function is invoked in MyController, I get the following error in the browser's console:
Error: Invalid type: 0x�
at n.r [as decode] (msgpack.min.js:1)
at n.fetch (msgpack.min.js:1)
at n.s [as read] (msgpack.min.js:1)
at Object.n [as decode] (msgpack.min.js:1)
at success ((index):46)
at angular.js:17396
at m.$digest (angular.js:18557)
at m.$apply (angular.js:18945)
at k (angular.js:12799)
at V (angular.js:13056) "Possibly unhandled rejection: {}"
It seems like the first HEX value \x93 is not being decoded.
I'm using kawanet/msgpack-lite in the front end as I found that it can be used in a browser.
Please help!
The error message tells you you sent the wrong type of object to the decode() method. The buffer decoding documentation shows that only buffers, arrays and Uint8Array objects are supported.
Change the response type to arraybuffer (from the default text), then feed the response data to msgpack.decode() as a Uint8Array typed array:
$http.get("/test", {responseType: "arraybuffer"}).then(function success(response) {
var arr = new Uint8Array(response.data);
console.log(arr);
$scope.text = msgpack.decode(arr);
}, function error(response) {
console.log(response);
});
While not necessary here, you do want to set a different content type on your Flask response. Currently the /test route is advertising the content as text/html, while you really should be labelling as application/msgpack:
#app.route('/test')
def test():
return msgpack.packb([1, 2, 3]), {'content-type': 'application/msgpack'}
my bottle app.py looks something like:
#route('/submit/<var>')
def submit(var):
# do action
redirect("/")
and i have a simple html button which redirects to the required page when clicked through
onclick="window.location.href='/submit/value'"
but this causes the whole page to effectively refresh by going to the link and then coming back straight away, putting the user to the top of the page and so on.
How can i get it to do the bottle backend without refreshing?
In your template:
<button type="button" onclick="onSubmit('Value')">Sumbit!</button>>
<p id="response"></p>
<script>
function onSubmit(value) {
var myRequest = new Request('/submit/' + value);
var myInit = { method: 'PUT' };
fetch(myRequest, myInit).then(function(response) {
return $("#response").text(response);
});
}
</script>
In the python side
#put("/submit/<val>")
def submit(val):
return "Yay it worked!"
I am writing a small web app to test my understanding of integrating Angular 1 and Flask. When I try to send output from my Python function to the URL Localhost/synthesize_data, I see a Flask error: 'NoneType Object is not Subscriptable." It is trying to run my Python script again, but I only want it to show the output that the function should already have generated.
HTML with Angular:
<!doctype html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.3/angular.min.js"></script>
<script src="http://code.angularjs.org/1.5.3/angular-route.min.js"></script>
<script>
var myApp = angular.module('myApp', [
'ngRoute',
]);
myApp.config(['$routeProvider',
function($routeProvider) {
$routeProvider.
when('/', {
templateUrl: '/static/partials/index.html',
}).
otherwise({
redirectTo: '/'
});
}]);
myApp.controller('formController', ['$scope', '$http', function($scope, $http) {
console.log('HA');
$scope.formData = {};
$scope.processForm=function() {
console.log("righthere")
$http({
method: 'POST',
url : '/synthesize_data',
data : $scope.formData,
headers: {'Content-Type': 'application/json'}
})
.success(function(data){
console.log(data);
})
};
}])
</script>
<!--<link rel="stylesheet" href="/static/css/style.css" />-->
</head>
<body ng-app="myApp" ng-controller="formController">
<h1>Data Synthesizer Startup Page</h1>
<div>
<form ng-submit = "processForm()">
<div id = "name-group" class = "form-group">
<label>Number of Rows to Create </label>
<input type = "text" name = "name" class = "form-control" placeholder = "Enter valid number input" ng-model = "formData.name">
<span class = "help-block"></span>
</div>
<button type = "submit" class = "btn btn-success btn-lg btn-block">
<span class = "glyphicon glyphicon-flash"></span> Submit
</button>
</form>
<!--<div ng-view></div>-->
</body>
</html>
Python code (initialize program with hello()):
from flask import Flask, send_file, request
from data_synthesis import *
app = Flask(__name__)
#app.route("/synthesize_data", methods=['GET', 'POST'])
def datasynthesize():
print ("Calling synthesizer for ", request.json['name'], " rows.")
main(int(request.json['name']))
return "TEST"
#app.route("/")
def hello():
print ("Python up and running.")
return send_file("templates/start.html")
if __name__ == "__main__":
app.run(debug=True)
What I want is to see "TEST" on the screen when I run this program and navigate to Localhost/synthesize_data. Instead, it attempts to run my Python program again without proper input, which throws the error.
To only execute the program when it has submitted form data you need to add a conditional into the view function. The code the way you have it is dependent on having the name value as part of the request.
Request.json is None if the mimetype is not application/json. This is what is throwing the 'NoneType' Object is not Subscriptable error when you're issue a GET request (e.g. when you navigate to localhost/synthesize_data). A better way of accomplishing this, per the Docs, is to use Request.get_json():
#app.route("/synthesize_data", methods=['GET', 'POST'])
def datasynthesize():
data = request.get_json()
if data:
print ("Calling synthesizer for ", data['name'], " rows.")
main(int(data['name']))
return "TEST"
I've been working on a Cordova app, using the Ionic framework. I'm trying to do a "File Transfer" to a my Python-Flask back-end, but I keep getting "400 Bad Request". I'm quite sure that the error is caused by the request parsing method in flask (I'm currently using "request.file[]").
But I can't seem to figure out how to parse the POST request properly.
The POST is send in chunkedMode, with the mime type "image/jpeg", not sure if this could have any impact (My Nginx proxy is set up to properly receive POSTs in chunked mode).
My client code:
$scope.getPhoto = function() {
$scope.modal.show();
navigator.camera.getPicture(
// Succes
function(imageURI){
// Upload image
var options = new FileUploadOptions();
options.fileKey = "file";
options.fileName = imageURI.substr(imageURI.lastIndexOf('/')+1);
options.mimeType = "image/jpeg";
options.trustAllHosts = true;
options.chunkedMode = true;
var ft = new FileTransfer();
// Initiate upload
ft.upload(imageURI, encodeURI("http://192.168.1.53/"),
// Succes
function(succes){
alert(succes.response);
console.log(succes.response);
},
function(error){
alert(error.target);
console.log(error);
},
options
);
},
// Error
function(message) {
console.log('get picture failed');
},
// Options
{
quality: 100,
encodingType: Camera.EncodingType.JPEG,
allowEdit : false,
correctOrientation: true,
destinationType: navigator.camera.DestinationType.FILE_URI,
sourceType: navigator.camera.PictureSourceType.PHOTOLIBRARY
}
);
I got this working in PHP, witht the following code:
if(isset($_FILES["somefile"]))
{
//Filter the file types.
if ($_FILES["somefile"]["error"] > 0)
{
echo "error:" + $_FILES["somefile"]["error"];
}
else
{
callSomeFunction();
}
} // End of function
My current flask app looks something like this:
#app.route("/", methods=["GET", "POST"])
def upload_file():
if request.method == "POST":
file = request.files["file"]
if file and allowed_file(file.filename):
filename = secure_filename(file.filename)
file.save(os.path.join(app.config["UPLOAD_FOLDER"], filename))
#fileStream = file.stream
#print(opencvDataFromStream(fileStream))
#processImage(filename)
return processImage(filename)
return """
<!doctype html>
<title>Upload new File</title>
<h1>Upload new File</h1>
<form action="" method=post enctype=multipart/form-data>
<p><input type=file name=file>
<input type=submit value=Upload>
</form>
"""
As you can see it returns a form, and when the POST is send through the form, the request is treated as expected.
But when the POST request is send through the cross side Cordova client, I get the bad request error.
Would any one happen to know how to fix this?
Sincerely a flask n00b.
So I screwed up...
The error was simply caused, due to the fact, that the fileKey ("imageToScan") parameter in the client wasn't the same fileKey as my Flask back-end was expecting ("file").