How to auto update value from Flask inside JS in html? - javascript

Im trying to get a sensor value from Flask to auto update in a javascript gauge. The sensor input is named "a1" in Flask python.
I got it to work in a regular html code like this:
<div id="main">
<h4>Sensor 1: <span class="bold white">{{a1}}</span> bar </h4>
and to auto update it every second with this:
$(document).ready(function(){
setInterval(function(){
$("#main").load(window.location.href + " #main" );
}, 1000);
});
and the value even shows up in the js gauge:
<div id="PT1" class="gauge-container pt">
<span class="label">Pressure Transmitter 1</span>
<span class="label2">0 - 400 bar</span>
</div>
JS:
var PT1 = Gauge(
document.getElementById("PT1"), {
max: 400,
dialStartAngle: 135,
dialEndAngle: 45,
label: function(value) {
return Math.round(value * 100) / 100;
}
}
);
(function loop() {
var value1 = {{a1}}
PT1.setValueAnimated(value1, 1);
setTimeout(loop, 1000);
})();
My problem is that the gauge value dont auto update, it only shows the correct value when I refresh the page, and stays unchanged until I refresh again. (while the html code keeps updating every second)
Is this possible to solve?
Thanks
Vincent

Here's my final working example code.
Python:
from flask import Flask,render_template, jsonify
from random import random
app = Flask(__name__)
#app.route('/read_sensor')
def read_sensor():
data = {'a1': (random()*150)}
return jsonify(data)
#app.route('/')
def index():
return render_template("index.html")
if __name__ == "__main__":
app.run(host='0.0.0.0', port=80, debug=True)
HTML
<body>
<div id="PT1" class="gauge-container">
<span class="label">DEFAULT</span>
</div>
</div>
<script type='text/javascript'>
var PT1 = new Gauge(
document.getElementById("PT1"), {
max: 400,
dialStartAngle: 135,
dialEndAngle: 45,
label: function(value) {
return Math.round(value * 100) / 100;
}
}
);
function callme(){
//This promise will resolve when the network call succeeds
//Feel free to make a REST fetch using promises and assign it to networkPromise
var networkPromise = fetch('/read_sensor')
.then(response => response.json())
.then(data => {
console.log(data);
PT1.setValueAnimated(data['a1'], 1);
});;
//This promise will resolve when 2 seconds have passed
var timeOutPromise = new Promise(function(resolve, reject) {
// 2 Second delay
setTimeout(resolve, 2000, 'Timeout Done');
});
Promise.all(
[networkPromise, timeOutPromise]).then(function(values) {
console.log("Atleast 2 secs + TTL (Network/server)");
//Repeat
callme();
});
}
callme();
</script>
</body>
Thanks!

You need to write some Javascript to query the endpoint periodically. I used the approach in this answer which makes a request every two seconds, without making a second call when a request is under way.
You'll need to build a Flask route which returns only JSON with the sensor data. I mocked this with random numbers:
import random
#app.route('/read_sensor')
def read_sensor():
# return the actual sensor data here:
return {'a1': random.choice(range(1,400))}
Then use the following JS which makes a request every 2000ms. I had to tweak your example slightly, changing the div to a canvas to get guage.js to work properly:
<html>
<body>
<div class="chart">
<canvas id="PT1"></canvas>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gauge.js/1.3.7/gauge.min.js" integrity="sha512-J0d1VfdfTSDoDPEsahCtf2nC+groXdWkuQFyJjS+s3CpKj63X9Hf3pMEJtjIJt/ODh0QwTRx2/OioL+9fMoqSA==" crossorigin="anonymous"></script>
<script type='text/javascript'>
//
// Guage init code
//
var PT1 = new Gauge(
document.getElementById("PT1"), {
max: 400,
dialStartAngle: 135,
dialEndAngle: 45,
label: function(value) {
return Math.round(value * 100) / 100;
}
}
);
//
// End guage init code
//
function callme(){
//This promise will resolve when the network call succeeds
//Feel free to make a REST fetch using promises and assign it to networkPromise
var networkPromise = fetch('/read_sensor')
.then(response => response.json())
.then(data => {
console.log(data);
//
// Set guage code
//
PT1.set(data['a1']);
//
// End set guage code
//
});;
//This promise will resolve when 2 seconds have passed
var timeOutPromise = new Promise(function(resolve, reject) {
// 2 Second delay
setTimeout(resolve, 2000, 'Timeout Done');
});
Promise.all(
[networkPromise, timeOutPromise]).then(function(values) {
console.log("Atleast 2 secs + TTL (Network/server)");
//Repeat
callme();
});
}
callme();
</script>
</body>
</html>
Here's what this renders like:

Thanks for your reply and example.
I just cant get your example to work with my gauge...
Can you please show an example of the rest of the flask route? Like the part with "return / render html"
Here is a simplified copy of my old Flask:
(that works, but not updating)
#app.route('/')
def index():
a1 = sensors()
return render_template("index.html", a1=a1)
def sensors():
a1 = "%0.2f" % (287.8 + random())
return a1
if __name__ == "__main__":
app.run(host='0.0.0.0', port=80, debug=True)
Isnt there an easy way to just get the "a1" to be updated inside the JS for the gauge?
(function loop() {
var value1 = {{a1}}
PT1.setValueAnimated(value1, 1);
setTimeout(loop, 1000);
})();
If I replace {{a1}} with "Math.random() * 100," then its updating every second.
Full example of the gauges here:
https://codepen.io/ernestoulloa/pen/abmGYyY (I am using gauge1)

Related

Pause function to wait for ajax request inside of a websocket

I have a WebSocket that adds people to a messaged list when they receive a message. I use a fetch to get information from the server to build the messaged list, but I cannot find an easy way to get the code to pause until the fetch completes. I cannot add await because the funciton is inside a websocket that isn't async. Does anyone have any suggestions on this?
socket.on('receive_message', function(data) {
let messaged_user_li = document.getElementById('messaged_user_li'+data['from_user']);
console.log('messaged_user_li: '+messaged_user_li)
if (messaged_user_li == null) {
console.log('if fired')
//THIS is the fetch I want to pause the function until complete. Note that the await here does not work since the funciton isn't async.
await fetch('/get_messaged_user/'+from_user).then((response) => {
response.json().then((data2) => {
loadMessagedUser(data2[0].id, data2[0].avatar, data2[0].username, data2[0].last_seen);
});
});
messaged_user_li = document.getElementById('messaged_user_li'+data['from_user']);
}
console.log('messaged_user_li: '+messaged_user_li)
let message_user = document.getElementById('message_user'+data['from_user']);
let message_target = document.getElementById("message_target"+data['from_user']);
if (messaged_user_li.classList.contains('active') == false) {
messaged_user_li.classList.add('flash-message');
}
if (message_user != null) {
data = `
<li class="clearfix">
<div class="message-data text-right">
<span class="message-data-time">just now</span>
</div>
<div class="message other-message float-right">`+data['message']+`</div>
</li>`;
message_target.innerHTML += data;
//Move scroller to bottom when message received
myDiv = message_user.querySelector(".chat-history");
myDiv.scrollTop = myDiv.scrollHeight;
}
});
Actually You can add async function in socket.on
socket.on('receive_message',async function(data) {

Using AJAX, JavaScript to call python flask function with return to JavaScript

I know that you can have javascript to call a python flask function and be able to return data to a designated id. Like this:
HTML
<div id = "update"> </div>
<script type="text/javascript">
var counter = 0;
window.setInterval(function(){
$("#update").load("/game?counter=" + counter);
counter++;
}, 5000)
views.py
from flask import request
#app.route("/game")
def live_game():
textlist = ['a','b','c','d']
counter = request.args.get('counter')
return "<p> " + textlist[counter] + " </p>"
I found this in a previous post. What I would like to do is update utilize this method in updating some cool justgage that I found online to show the most up to date temperature and humidity readings from my database. Here is the script that I was wanting to use:
HTML
<div class="container">
<div class="jumbotron">
<h1>Historical Environmental Readings</h1>
<div class="container-fluid" id="dht-container">
<div id="g1" style="width: 200px; height: 150px;"></div>
<div id="g2" style="width: 200px; height: 150px;"></div>
</div>
</div>
</div>
.....
<script>
function ajaxd(NodeID) {
//reload result into element with id "dht-container"
$(??????).load("/tempnodeDHT", function() { alert( "Temp Load was performed." ); });
document.addEventListener("DOMContentLoaded", function (event) {
var g1 = new JustGage({
id: "g1",
value: 50,
min: -20,
max: 150,
title: "DHT Temp",
label: "temperature",
pointer: true,
textRenderer: function (val) {
if (val < NODE_EnvCP.low_temp) {
return 'Cold';
} else if (val > NODE_EnvCP.hot) {
return 'Hot';
} else if (val === NODE_EnvCP.optimum_temp) {
return 'OK';
}
},
});
var g2 = new JustGage({
id: "g2",
value: 50,
min: 0,
max: 100,
title: "Target",
label: "Humidity",
pointer: true,
textRenderer: function (val) {
if (val < NODE_EnvCP.low_hum) {
return 'LOW';
} else if (val > NODE_EnvCP.high_hum) {
return 'HIGH';
} else if (val === NODE_EnvCP.optimum_hum) {
return 'OK';
}
},
});
setInterval(function () {
(currentTemp, currentHumidity)=ajaxd();
g1.refresh(currentTemp);
g2.refresh(currentHumidity);
return false;
}, 2500);
});
</script>
This is my python flask function:
run.py
#app.route('/nodeDHT')
def getLatestDHT():
NodeID = request.args.get('NodeID')
df = DAO.Pull_CURRENT_DHT_Node_Data(self, NodeID)
currentTemp = df.Temperature[0]
currentHumidity = df.Humidity[0]
return (currentTemp, currentHumidity)
I was hoping that I could change the ?????? inside
$(??????).load("/nodeDHT", function() { alert( "Temp Load was performed." ); });
so that the two variables (currentTemp, currentHumidity) would end up back into the javascript portion so that the gages would update every 2.5 seconds. Also, am I passing the variable back to the python flask? there is a variable already pushed to the html when it was rendered.
EDIT:
could I do something like this:
#app.route('/nodeDHT')
def getLatestDHT():
NodeID = request.args.get('NodeID')
df = DAO.Pull_CURRENT_DHT_Node_Data(self, NodeID)
currentTemp = df.Temperature[0]
currentHumidity = df.Humidity[0]
return json.dumps(currentTemp, currentHumidity)
and in the javascript side do something like this?
function ajaxd(NodeID) {
//reload result into javascript
$.get("/nodeDHT",function( currentTemp, currentHumidity ){ console.log($.parseJSON(currentTemp, currentHumidity)});
What I'm really asking is. How can I pass single/multiple variables to the python flask function from the javascript function and then get back a dataframe where I can use column values to update a chart or multiple variables back to the javascript function to be used in a setinterval to be used for multiple functions such as updating justgage
setInterval(function () {
(currentTemp, currentHumidity)=ajaxd();
g1.refresh(currentTemp);
g2.refresh(currentHumidity);
return false;
}, 2500);
---------------------DOUBLE EDIT -----------------------
COULD I DO SOMETHING LIKE THIS:
function UPDATEGAGES(NodeID) {
$.get('/nodeDHT?NodeID='+NodeID+'&startdatetime='+startdatetime,
function(data){ const parsed = JSON.parse(data)};
g1.refresh(currentTemp);
g2.refresh(currentHumidity);
});
setInterval(function () {
UPDATEGAGES(NodeID);
return false;
}, 2500);
If you want to send variables to server in get method, use variables in url
'/tempnodeDHT?NodeID='+your_nod_id+'&anotherVar='+value+'&anotherVar2='+value2
You can access them in your flask server as below
NodeID = request.args['NodeID']
anotherVar = request.args['anotherVar']
anotherVar2 = request.args['anotherVar2']
If you want to send multiple variables from flask server to your front end, send it as JSON
return jsonify({'currentTemp': currentTemp, 'currentHumidity': currentHumidity })
When you handle it the response from the server, again parses it to JSON.
$.get( '/nodeDHT/tempnodeDHT?NodeID='+your_nod_id+'&anotherVar='+value+'&anotherVar2='+value2, function( data ) {
const parsed = JSON.parse(data)
// here you can access your 'currentTemp' variable from parsed.currentTemp
});
EDIT
For your final desired output
function UPDATEGAGES(NodeID, startdatetime) {
$.get('/nodeDHT?NodeID='+NodeID+'&startdatetime='+startdatetime,
function(data){
const parsed = JSON.parse(data)
g1.refresh(parsed.currentTemp);
g2.refresh(parsed.currentHumidity);
};
)
};
setInterval(function () {
UPDATEGAGES(NodeID, startdatetime);
return false;
}, 2500);

Is there a way to call a django function without refreshing the page

I'm trying to create a function that generates a random value in django so that it can be recorded, and then is passed into a javascript file where it spins a wheel to show the animation, and then after the animation is over puts 10 coins into the profiles account. The problem is when I use a view function, the return type is a page redirect, so the animation gets cut off. Is there a type of django function that can parse in values without a redirect, similar to a C++ void function?
Here's the code I have so far
views.py
#login_required
def Red(request):
profile = get_object_or_404(Profile, user=request.user)
profile.coins += 10
profile.save(update_fields=['coins'])
random = random.randrange(11)
return render(request, "bets/bets.html", {'random' : random})
Profile model:
from django.db import models
from django.contrib.auth.models import User
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
coins = models.DecimalField(max_digits=9, decimal_places=2, default=0.00)
def __str__(self):
# #ts-ignore
return f'{self.user.username} Profile'
and the html with the javascript in it
<div class="Container">
<div class="inputContainer" blank="False">
{% csrf_token %}
<span class="coinContainer2"><i class="fas fa-coins"></i></span>
<input name="amount" class="amountInput" type="number" min="0.01" placeholder="Enter Amount">
<div class="sideContainer" id="buttonNav">
<span class="redButton"><button type="button" class="btn btn-danger" id="buttonRed" name = "redButton"> Red </button></span>
</div>
</div>
</div>
</div>
<script>
var itemSelected = 0;
var stoping = false;
jQuery(function ($) {
var $owl = $('.owl-carousel');
// Initialize Owl
$('.owl-carousel').owlCarousel({
center: true,
loop: true,
margin: 10,
nav: false,
mouseDrag: false,
touchDrag: false,
pullDrag: false,
dots: false,
responsive: {
0: {
items: 3
},
600: {
items: 3
},
1000: {
items: 7
}
}
});
// Click in button Jump
$('#buttonNav').click(function (e) {
e.preventDefault();
stoping = false;
// Random between 1 and 10
//var itemSelected = Math.floor((Math.random() * 11));
itemSelected = "{{ winner }}";
var $jump = $(this);
//$jump.html('Jumping ...');
$jump.attr('disabled', 'disabled');
// Trigger autoplay owl
$owl.trigger('play.owl.autoplay', [100]);
// Slow speed by step
setTimeout(slowSpeed, 2000, $owl, 200);
setTimeout(slowSpeed, 4000, $owl, 250);
setTimeout(slowSpeed, 5000, $owl, 300);
setTimeout(stopAutoplay, 6000);
return false;
});
// Event changed Owl
$owl.on('changed.owl.carousel', function (e) {
if (stoping) {
// Examine only if roulette stop
var index = e.item.index;
var element = $(e.target).find(".owl-item").eq(index).find('.element-roulette');
var item = element.data('item');
if (item == itemSelected) {
// If element equals to random, stop roulette
$owl.trigger('stop.owl.autoplay');
//$('#buttonNav').html('Jump');
$('#buttonNav').removeAttr('disabled');
}
}
});
// Showcase
slowSpeed($owl, 1400);
});
/**
* Reduce speed roulette
* #param {type} $owl
* #param {type} speed
* #returns {undefined}
*/
function slowSpeed($owl, speed) {
$owl.trigger('play.owl.autoplay', [speed]);
}
/**
* Stop autoplay roulette
* #returns {undefined}
*/
function stopAutoplay() {
stoping = true;
}
</script>
Thanks!
I believe it's only possible is though JavaScript or jquery.
https://api.jquery.com/jQuery.get/
URL to your function.
Idk know how different flask is from django but I use this tutorial to figure this out myself a week ago.
https://flask.palletsprojects.com/en/1.1.x/patterns/jquery/

Loading page info into array, and cycling page info as URLs

I;ve built a CMS that allows users to build static pages of images and text content solely for displaying on television screens throughout our building. I've completed this to the point of viewing the display with it's pages but only if I call it explicitly in the url. The problem is, I want to load by display which is stored in the URL.
For instance, the url now if you click on Display 3 is http://local.CMSTest.com/showDisplay.php?display=3
and this calls a function using 3 as the display ID that grabs all pages with that display ID. This works, but in my html where I throw a foreach in there, it loads both pages associated with that display into a crammed page, half and half. So I know it's working but I need to store these pages into an array for javascript as well I believe.
I'm thinking there may be a way where I can load it with the first page on default and append it to the url like http://local.CMSTest.com/showDisplay.php?display=3&page_id = 2 and then after the time is up, it can go to the next one and change the URL http://local.CMSTest.com/showDisplay.php?display=3&page_id = 3
So the PHP and HTML is working at this moment:
<div class="row top">
<?php include 'banner.php'?>
</div>
<div class="row middle" style="background-image: url(<?php echo $showDisplays['background_img']?>);">
<div class="col-lg-12">
<?if($showDisplays['panel_type_id'] == 1){?>
<div class="fullContent" style=" height: 100%; ">
<?php echo $showDisplays['content']?>
</div>
<?}?>
</div>
</div>
<div class="row bottom">
<?php include 'ticker.php';?>
</div>
<?php }?>
I want to try some javascript like this that will take each page from the array, replacing the following URLs with page IDs from my previous array. How can I do this properly?
<script type="text/javascript">
var Dash = {
nextIndex: 0,
dashboards: [
{url: "http://www.google.com", time: 5},
{url: "http://www.yahoo.com", time: 10},
{url: "http://www.stackoverflow.com", time: 15}
],
display: function()
{
var dashboard = Dash.dashboards[Dash.nextIndex];
frames["displayArea"].location.href = dashboard.url;
Dash.nextIndex = (Dash.nextIndex + 1) % Dash.dashboards.length;
setTimeout(Dash.display, dashboard.time * 1000);
}
};
window.onload = Dash.display;
</script>
UPDATE:
Current array
Array ( [pageID] => 104 [page_type_id] => 1 [display_id] => 3 [slide_order] => [active] => 1 [background_img] => [panel_id] => 96 [panel_type_id] => 1 [page_id] => 104 [cont_id] => 148 [contID] => 148 [content] =>This is full content)
First you would need to access your php array from javascript and you can do that by using php's json_encode which will convert the array to a JSON object.
Then you can use URL search params to alter the query parameters for the next URL which you can call. See the below example
function setDisplay() {
let params = new URL(document.location).searchParams;
let pageID = params.get("pageID");
let disply = params.get("display");
// set the html based on pageID and display
}
function getNextURL() {
// encode your php array to json
// let obj = <?php echo json_encode($myArray); ?>;
// probably what obj will look like after above line
// this is just for this example, you should use the line commented out above
var obj = {
"pageID": 104,
"page_type_id": 1,
"display_id": 3,
}
let params = new URL(document.location).searchParams;
params.set("pageID", obj.pageID);
params.set("display", obj.display_id);
let url = window.location.href.split('?')[0];
let nextURL = url + "?" + params.toString();
console.log(nextURL);
return nextURL
}
getNextURL();
You would need to
parse the frames.displayArea.location.href for its query parameters.
assign to Dash.nextIndex either (params.page_id + 1) % Dash.dashboards.length because the page_id is the currently diplaying page and we want the next page... or 0 if params.page_id is undefined.
build your URL from dashboard.url appending the display_id and page_id parameters.
For cleanliness, I recommend creating a var called newURL since we need to build a custom string.
display: function() {
// step 1
let queryString = frames.displayArea.location.href.split("?")[1];
let paramArr = queryString.split("&");
let params = {};
paramArr.forEach((param) => {
let [name, value] = param.split("=");
params[name] = value;
})
console.log("params: ", params)
// step 2
Dash.nextIndex = (params.page_id)
? (params.page_id + 1) % Dash.dashboards.length
: 0;
var dashboard = Dash.dashboards[Dash.nextIndex];
// step 3
let newURL = dashboard.url + "?display_id=" + params.display_id
+ "&page_id=" + Dash.nextIndex;
// Unrelated nit: bracket access IMHO should only be used when specifying a
// property by the string value contained in a variable, or when the
// property name would be an invalid javascript variable name (starting
// with a number, including spaces, etc)
frames.displayArea.location.href = newURL;
setTimeout(Dash.display, dashboard.time * 1000);
}

Javascript not working when Alert is removed

I use D3.js and upon loading data from csv file the page isn't rendering when I remove the alert() statements. I;'m not a js specialist and I know it's something to do with asynchronous modes of operation but I don't know what to change.
when alert("AAAAAAAA"); is removed it works I appreciate this is a bespoke
issue so the other posts on the subject were not easily understood.
Many Thanks
Andy
</style>
<div id='dashboard2' align="center"></div>
<div id='dashboard' align="center"></div>
<script src="/xxxxxxxx/templates/uber/js/d3.min.js"></script>
<script type="text/javascript">
var climate_csv = function()
{
d3.csv("/xxxxxxxx/templates/uber/js/theclimate.csv", function(climate_statistics)
{
//prices is an array of json objects containing the data in from the csv
console.log("climate_statistics:", climate_statistics)
climate_data = climate_statistics.map(function(d)
{
//each d is one line of the csv file represented as a json object
console.log("d", d)
month = d.month;
console.log("month:", d.month, month)
low = +d.low;
console.log("low:", d.low, low)
high = +d.high;
console.log("high:", d.high, high)
rainfall = +d.rainfall;
console.log("rainfall:", d.rainfall, rainfall)
sunshine = +d.sunshine;
console.log("sunshine:", d.sunshine, sunshine)
nighttime = +d.nighttime;
console.log("nighttime:", d.nighttime, nighttime);
return {"Month": month, "cStats": {"low": low , "high": high} , "rainfall": rainfall , "sun":{"Sunshine": sunshine , "Nighttime": nighttime}}
})
})
}
</script>
<script type="text/javascript">
var climateStats2=[
{TempRange:'Low',cStats:{low:0}}
];
var climateStats3=[
{TempRange3:'High',cStats:{high:0}}
];
var climateStats4=[
{TempRange4:'Rainfall',cStats:{rainfall:0}}
];
var climateStats5=[
{TempRange4:'Rainfall',cStats5:{lower5:0, upper5:0}}
];
climate_csv();
alert("AAAAAAAA");
dashboard('#dashboard',climate_data,climateStats2,climateStats3,climateStats4,climateStats5);
Seems like your data is not fully loaded, when you call dashboard. It works with the alert because while the alert is open, your script is paused. Another thing is, that you are using global variables here. Let's avoid this using callbacks.
D3's csv function uses a callback and calls it, when the data is ready.
d3.csv("/xxxxxxxx/templates/uber/js/theclimate.csv", function(climate_statistics) {
/* ... */
});
You can now either call the dashboard function inside the callback, or add a callback to your function. Let's do the latter here:
var climate_csv = function( callback ) {
d3.csv(
"/xxxxxxxx/templates/uber/js/theclimate.csv",
function( climate_statistics ) {
var climate_data = climate_statistics.map(function( d ) {
month = d.month;
low = +d.low;
high = +d.high;
rainfall = +d.rainfall;
sunshine = +d.sunshine;
nighttime = +d.nighttime;
return {
"Month": month,
"cStats": {
"low": low,
"high": high
},
"rainfall": rainfall,
"sun": {
"Sunshine": sunshine,
"Nighttime": nighttime
}
};
});
// we are done with mapping our data
// let's call the callback with the data
callback(climate_data);
});
};
/* ... */
climate_csv(function( climate_data ) {
dashboard('#dashboard', climate_data, climateStats2, climateStats3,
climateStats4, climateStats5);
});
Put the function that is not working when you remove the alert inside the success callback, that will make the trick.
.csv works async, so when you put the alert, the request is completed on the background, when you remove it no "wait time" is added (as it should be) and that's why it's not working.
Not using d3.js, but this doc could help:
https://github.com/mbostock/d3/wiki/CSV
d3.csv("path/to/file.csv")
.row(function(d) { return {key: d.key, value: +d.value}; })
.get(function(error, rows) { console.log(rows); });
Hope it helps.

Categories