Vincent
Vincent

Reputation: 33

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

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

Upvotes: 1

Views: 1567

Answers (3)

Vincent
Vincent

Reputation: 33

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!

Upvotes: 2

v25
v25

Reputation: 7621

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:

guage gif

Upvotes: 0

Vincent
Vincent

Reputation: 33

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)

Upvotes: 0

Related Questions