Agent served web page with push updates

Is there a simple way to serve push live updates of a value from the agent to a web client instead of using refresh/polling? I am looking for a “few lines of code” solution without additional own server setup.

Here an example based on this recent blog post https://community.electricimp.com/blog/how-to-serve-an-html-form-via-an-agent-and-deal-with-the-results/

Changes to a value on the imp is sent to the agent, serving the latest value to a web client, but the client has to press the browser refresh button to get updates.

Agent nut:
`
a_value <- 0;

function setPage(){
local page = @"

My Web Page

My headline

Value is now: " + a_value + @"

";

return page;
}

http.onrequest(function(request, response)
{
response.send(200, setPage());
});

device.on(“update”, function(value){a_value = value;});
`

Device.nut:
`
function simulatingStatusUpdates(){
value = time() % 60;
server.log("Value now: " + value);
agent.send(“update”, value);
imp.wakeup(7, simulatingStatusUpdates);
}

value <- 0;
simulatingStatusUpdates();
`

There’s a few options… you could use services like PubNub or Firebase (both have free developer plans)

Alternatively, you could use a technique called long polling - where the webpage makes a request to the imp, which doesn’t respond until there’s new data. I don’t think I have an example handy for this - but I’ll make up something today and post it :slight_smile:

In the meantime, do check out Firebase and PubNub - they are excellent services and very powerful / extendable :slight_smile:

Few, if any, will be surprised I suggest looking at GroveStreams. We push all our data there and have a multitude of web Dashboards defined that automatically refresh on the specified period. We usually set them to update every 5 minutes, but often set the widget(s) to update faster when we’re looking for “live” data. We normally keep several of the Dashboards up throughout the day to keep an eye on our clients’ sites. The GroveStreams costs are minimal, if any, depending upon what you’re doing. I’d be surprised if Xively and others don’t offer the same kind of auto-update presentation capability.

Thanks! Great to learn about these options. PubNub, Firebase and GroveStreams looks like powerfull alternatives. A simple long polling example sounds great for briefly monitoring the occasional change of a variable during testing.

You could also have your website refresh itself every 1-2 minutes automatically.

Add this to the top of your webpage:
`

`

Yes, good for “less live” data. Would like to see more close to real-time updates. Changing CONTENT="120" to "0.5" seems rude, when it might be around 3 minutes before the next update :slight_smile:

The only other non polling method (so to speak) is using something like MQTT where the agent publishes data and then your client based system subscribes to the data set. The catch is that you need to use an MQTT broker in between. A useful “GroveSteams” alternative is www.carriots.com which now includes an MQTT option if you want to try this method.

Anyhow, not sure why you dislike polling option unless you are planning to have web client open all of the time… that is why servers don’t push data… as client links change / close / timeout etc.

For polling option, here is a very straight forward java script I use which I bury inside my agent html page:

function grabData() { $.post(document.URL, {... place your key:value pairings here... }, function(data) { ... javascript code to process the data you receive back .....}, 'json') .always(function() { setTimeout(grabData, 20000); }) .fail(function(data) { alert('ERROR: ' + data.responseText); }); }

This will poll every 20 seconds from the client side without needing a page refresh and talks to Imp agent directly - no additional own server setup required. Then in agent you manage post data received as per usual through http.onrequest handler.

@gerriko thank you for this - looks great. I guess rapid polling feels wasteful having developed embedded devices for many years, where ultimately it can be bad for battery life.

Polling is done at client side (web-browser / phone) and talks to agent/cloud - hence embedded device is not impacted… although I do see you are essentially “polling” at device level by getting device to wakeup every 7 seconds.

I just happened to be looking at your code and here are some suggested mods (a bit of a cut n past hack so hopefully all there and correct). I’m leaving Device code as assuming correct, although it may require more thought if you were hoping to sync device wakeup timer with web page update. If the device data is only needed when the client page is open then you could rely on client side polling to handle timed requests. But I’ll leave that up to you.

Agent.nut
`
a_value <- 0;

function setPage(){
local page = @"

My Web Page

My headline

    <p>Value is now:<span id='new_val'></p>
</body>

<script>
     $(function() {  grabData();  });     // this will be called when document is ready
     
     function grabData() {
          $.post(document.URL, {gd: 'abcd1234', function(data) {
          $('#new_val').html(data.AV);
          }, 'json')
     .always(function() { setTimeout(grabData, 7000); })     // polls every 7 secs
     .fail(function(data) { alert('ERROR: ' + data.responseText); });
     }
   </script>
";

return page;
}

http.onrequest(function(request, response)
{
try
{
local method = request.method.toupper();
local DeviceConnected = true;

    response.header("Access-Control-Allow-Origin", "*");
    
    if (method == "POST") 
    {
        local data = http.urldecode(request.body);
        if (data.gd== "abcd1234") {
            response.send(200, http.jsonencode(AV=a_value ));
        }
    }
    else
    {
      response.send(200, setPage());
    }
}
catch(error)
{
    response.send(500, "Internal Server Error: " + error)
}

});

device.on(“update”, function(value){a_value = value;});
`

That is what I am looking for - nice and simple, but not working as is.

Opening the web page reads “My Web Page” and “Value is now:” but no value turns op. A server.log(“request”); before “try” indicates no grabData activity. I am not fluent in javascript but will see if I can figure it out …

Yes, device code is only for testing the rest.

OK, no doubt an error crept in somewhere.

I like to use Google Chrome of debugging. When opening the web-page, right click on the mouse button and select “Inspect element”. If we have javascript errors then these often show up in the “console” section. You then click on code line on right hand side and it will flag up where the error is. Otherwise if nothing in console, also check top right hand corner as often a little red number comes up which are the number of errors found. Hope that helps.

Then if nothing shows there, then check if message getting through using server.log in agent side etc.

Looking closer, I see this is still asynchronous long polling, and I am after sync, but that is just a matter of saving the response handle until there is an actual change from the device …

Just dug out a spare imp and uploaded my code hack. Found a few silly errors.

Firstly forget to add in the obvious… this goes in the “head” part of html page:

`

My Web Page `

Then one other syntax error in javascript… left out a curly bracket “}”

$.post(document.URL, {gd: 'abcd1234'}, function(data) {

Then finally another error in agent code… forgot the curly brackets again…

response.send(200, http.jsonencode({AV=a_value} ));

Here is cut n paste of revised agent code:

`
a_value <- 0;

function setPage(){
local page = @"

My Web Page

My headline

<p>Value is now: <span id='new_val'></p>
";

return page;
}

http.onrequest(function(request, response)
{
try
{
local method = request.method.toupper();
local DeviceConnected = true;

    response.header("Access-Control-Allow-Origin", "*");
    
    if (method == "POST") 
    {
        local data = http.urldecode(request.body);
        if (data.gd== "abcd1234") {
            
            response.send(200, http.jsonencode({AV=a_value} ));
        }
    }
    else
    {
      response.send(200, setPage());
    }
}
catch(error)
{
    response.send(500, "Internal Server Error: " + error)
}

});

device.on(“update”, function(value){a_value = value;});

`

Regarding syncing. Yes you have basically got it regarding saving of response handle. There was another recent post in the developer forum that discussed how to do this.

Thanks, that works. When data changes come at random times and sometimes in very quick succesion (and before the next http post arrives), a buffer is needed for device indications of data changes (and for http.posts) …