Code Example to Use Google Charts with Xively Data

This will run on the Agent side. You don’t need any Imp code to test. This will display a 2 line graph using 2 of your streams all you need to change is the Stream Name and add your API key and Feed Id. There is also an example of just displaying the xively PNG graph on the page, just uncomment those 2 lines to see those graphs.

`
Stream1Name <- “Your_Stream_Name_1”;
Stream2Name <- “Your_Stream_Name_2”;

stream1 <- null;
stream2 <- null;

htmlresp <- “”;

const FEED_ID = “YOUR_FEED_ID”;
const API_KEY = “YOUR_API_KEY”;

http.onrequest(function(req, resp) {
if (req.body == “”){
gethtmlresponse();
resp.send(200, htmlresp);//send this if just site was accessed and no requests

}

});

function gethtmlresponse(){

//Google Line Chart
local T1Data = blob(644);
local T2Data = blob(644);
getStreamData(FEED_ID)
local cnt = 0;

foreach(datapoint in stream1.datapoints) {
T1Data.writen(datapoint.value.tofloat(),‘f’);
cnt++;
//server.log(datapoint.value.tofloat());
}
foreach(datapoint in stream2.datapoints) {
T2Data.writen(datapoint.value.tofloat(),‘f’);
//server.log(datapoint.value.tofloat());
}
server.log("Number of Data Points: " + cnt);
T1Data.seek(0,‘b’);
T2Data.seek(0,‘b’);

htmlresp = “\r
”;
htmlresp += “\r
”;
htmlresp += “\r
”;

//from google chart example for line chart
htmlresp += “<script type=“text/javascript” src=“https://www.google.com/jsapi”>\r
”;
htmlresp += “<script type=“text/javascript”>\r
”;
htmlresp += “google.load(“visualization”, “1”, {packages:[“corechart”]});\r
”;
htmlresp += “google.setOnLoadCallback(drawChart);\r
”;
htmlresp += “function drawChart() {\r
”;
htmlresp += “var data = google.visualization.arrayToDataTable([\r
”;
htmlresp += “[‘Count’, '” + Stream1Name + “’, '” + Stream2Name + “’],\r
”;
local dt = 6.0;
local dtm = “”;
local t = 0;
for (t = 0; t < cnt; t++){
try{
htmlresp += “[’” + (cnt - t).tostring() + "’, " + T1Data.readn(‘f’) + ", " + T2Data.readn(‘f’) + “],\r
”;
}
catch(e){
break;
}
}
htmlresp += “]);\r
”;
htmlresp += “var options = {\r
”;
//htmlresp += “title: ‘Graph Title’,curveType: ‘function’,legend: { position: ‘bottom’ }};\r
”;
htmlresp += “title: ‘Graph Title’,”;
htmlresp += “curveType: ‘function’,”;
//htmlresp += “chartArea:{left:10,top:10,width:‘100%’,height:‘500’}”;
htmlresp += “};\r
”;
htmlresp += “var chart = new google.visualization.LineChart(document.getElementById(‘chart_div’));\r
”;
htmlresp += “chart.draw(data, options);\r
”;
htmlresp += “}\r
”;
htmlresp += “\r
”;

htmlresp += “\r
”;

//display google chart with 2 steams on 1 chart
htmlresp += “<div id=“chart_div”>\r

//get Xively png chart for each stream if you want
//htmlresp += “<img src=“https://api.xively.com/v2/feeds/” + FEED_ID + “/datastreams/” + Stream1Name + “.png?c=2188c5&g=true&t=%22” + Stream1Name + “%22&b=true&scale=manual&min=-25&max=100&h=200”>
”;
//htmlresp += “<img src=“https://api.xively.com/v2/feeds/” + FEED_ID + “/datastreams/” + Stream2Name + “.png?c=FFCC33&g=true&t=%22” + Stream2Name + “%22&b=true&scale=manual&min=-25&max=100&h=200”>
”;

htmlresp += “\r
”;
htmlresp += “\r
”;

}

function getStreamData(feedID){

local trigger_url = "https://api.xively.com/v2/feeds/" + FEED_ID + ".json?duration=6hours&interval=0&limit=600";
//may need to play with the limit=600 number to make sure you are getting all the available reading for last 6 hrs.
local req = http.get(trigger_url, {"X-ApiKey":API_KEY, "User-Agent":"xively-Imp-Lib/1.0"});     //add headers
local res = req.sendsync();         //send request
//server.log(res.body);
local data = http.jsondecode(res.body);
local dataStreams = data.datastreams;


foreach(s in dataStreams) {
    if (s.id == Stream1Name) {
        server.log("Xively Stream: " + s.id + " found.");
        stream1 = s;
    }
    if (s.id == Stream2Name) {
        server.log("Xively Stream: " + s.id + " found.");
        stream2 = s;
    }
}

return;

};`

I wasn’t too sure if I was using the blob correctly so I just bumped the number in the definition to 644 to make sure I had enough. I think since I am getting 72 readings for each stream and a float number is 4 bytes, I would need 288 in my blob. Any tweeks or help is appreciated. Next I want to get Date instead of just a count on the bottom of the chart.

This looks cool. Thanks. I’m trying it out.

I’m getting a build error at:

local trigger_url = "https://api.xively.com/v2/feeds/&quot; + FEED_ID + ".json?duration=6hours&interval=0&limit=600";

I have added my own feed id, key, and two stream names. What else might i need to change to make this work?

Forum gremlins. Try replacing &quot; with a double quote symbol.

Thanks @philmy. I got it to build and graph. Not sure I used the “double quote” as you suggested, though. Got this working with a single ".

Interesting thing is the data doesn’t seem to match my two sensors and the Xively data. I guess I should play with it and see why.

The temp from one sensor is off by about 5 degrees and the other sensor is off by a few tenths. I’m not sure why right now but will see what I can come up with…

EDIT - Yup. Data matches. It is important to play with the numbers in the code as @ljbeng said. I wasn’t displaying current data.

Thanks again guys!

So, don’t laugh, but I have spent most of the day on implementing this code. I love what @ljbeng has shown. It was working, but I did something which broke it.

It was operational for a couple hours, then I changed the Xively stream names, then changed the code above with the new stream names, expected that to work, but haven’t been able to load the agent webpage since…

I changed the Xively stream names back to the original, but still not working. I’m getting errors as shown (what is etiquette on log lines, do i wrap them with code tags?):

2014-07-25 16:02:30 UTC-4 [Agent] Xively Stream: _maxim_temp_1 found. 2014-07-25 16:02:30 UTC-4 [Agent] Xively Stream: _maxim_temp_2 found. 2014-07-25 16:02:30 UTC-4 [Agent] ERROR: the index 'datapoints' does not exist 2014-07-25 16:02:30 UTC-4 [Agent] ERROR: at gethtmlresponse:368 2014-07-25 16:02:30 UTC-4 [Agent] ERROR: from unknown:352

In my code, line 368 is

foreach(datapoint in stream1.datapoints) {

In my code, line 352 is

gethtmlresponse();

When it was working, almost, earlier in the day I had been tweaking the blob numbers trying to get the data to display close to real time. Problem is, when working best, it looked like my data was still 3 hours old. I know because i have been sequentially spiking temps on my two sensors to identify readings vs actual time. Is some global time zone thing involved in creating that ~3hour discrepancy? Or, do I simply need to ask more questions about “blobs”?

I have an imp posting to Xively every minute. Based on the calcs suggested by @ljbeng, that’s I guess 60 (minutes) x 6 (hours) x 4 = 1440?

I also played a lot with the limit number and the agent displayed graphs fine when it was 1000 or below, but anything over 1000 and the agent page wouldn’t load. What is that number, and how does it relate to “blobs”?

+ FEED_ID + ".json?duration=6hours&interval=0&limit=600";

Ok, I have an update. I used the same code in the agent file of another imp, with it’s own Xively profile. Worked fine.

I think I have learned in this experiment that it is important to not change the reporting frequency to Xively (by the imp) after you have this set up. I have also learned that changing the stream names is not cool.

I’m up and running with another setup and it works. I will try this again. Cool to be graphing in the agent page, for sure.

Do you have it working? I just saw more posts here. I have google gauge working nicely too and will post soon. I am also going to get the date or at least the hours on the bottom of the graph…

@ljbeng, well, it’s graphing, but not 6 hours of data and not real-time data. It’s data a few hours old, as far as i can tell, and only an hour and a half of it, it seems. I guess i’m really not sure how to tweak your code. If I set the “limit” number over 1000, the agent page won’t load.

I have one of MakeDeck’s 002 devices waking and posting data to Xively every 30 seconds.

By the way, this is unrelated, but what would we need to do to make the browser reload the graph on some interval, say 30 seconds or 1 minute?

Thanks again for offering your code. I’m very appreciative. I would much rather graph data in the agent page, as opposed to using 3rd party sites.

–EDIT-- Also, @ljbeng, if you have a moment and the inclination, I’m curious how the code might be modified to show other periods of data, like 1 hour, 3 hours and 12 hours. My current project involves using temperature sensors to monitor the water supply to my house and see if I can come up with a way to detect excessive water use. Playing with time period displayed is key. I was going to buy a flow sensor, but decided to try using temperature first as a low-cost proxy.

Also, I attached a pic (and another pic showing what was working before). The .png graphs were working, but now I look at the agent page and they are no longer displaying. I’m pretty sure I didn’t change any code, but I can’t promise I didn’t. I guess I simply mention this to see if you are also seeing a problem now with the .png image generation and display?

Xively “rules” state that it can only send 1000 events max. To get more than 6 hours of data the code has to send multiple reads using the last event time read as the begin of data for the next group OR you change the interval value and get more data. I want to do this as well but have not spent any time on it yet.

https://xively.com/dev/docs/api/quick_reference/historical_data/

for refresh use after head:

htlmresp += "<meta http-equiv=\"refresh\" content=\"30\">\\r\ "

I just tried this and got more data but need to investigate the actual dates.

?duration=5days&interval=300&limit=1000

I’m not saying anything negative about Xively. It’s certainly a time-proven and popular platform. However, much of what is being attempted here is pretty easy to do natively in Grovestreams’ dashboards (disclaimer – I have no connection to Grovestreams other than being a very satisfied user).

@ljbeng, THANKS!! I will test your modifications today. And I do look forward to timestamps. It will really help in tweaking my own setup. Great work.

@ctmorrisonhvacsp, yeah, 3rd party sites do good graphing already. But I guess I also like the idea of not being slave to a 3rd party. I finally did start looking at Grovestreams today. Looks good. Thanks for the tip. It was easy to set up. My only obstacle now is that the 1-wire code I have been using (from Tutorial) needs to be modified in order to send more than one temperature to Grovestreams. Made some progress on that, but I think I need to start a new thread. Somewhat embarrassing since I’m totally dependent on forum members for code :wink:

Here is the same graph and 2 Google Gauges underneath…

I liked Google Graphing/Gauges because I wrote an Ipod App that lets the user set the trigger points and the code below easily allows the alarm trip points to be changed on a whim, then the Red Critical area of the gauge changes with the users settings.

`
Stream1Name <- “Your_Stream_Name_1”;
Stream2Name <- “Your_Stream_Name_2”;

Stream1Trigger <- 60;//The trigger is where the red area begins
Stream2Trigger <- 80;

Stream1Compare <- “>”;//compare creates a red area either above or below the trigger point
Stream2Compare <- “<”;

Stream1GaugeMax <- 100;//How much range the gauge will need
Stream1GaugeMin <- 0;
Stream2GaugeMax <- 100;
Stream2GaugeMin <- 0;

Gauge1CurrentValue <- 40;
Gauge2CurrentValue <- 80;

stream1 <- null;
stream2 <- null;

htmlresp <- “”;

const FEED_ID = “FEED_ID”;
const API_KEY = “API_KEY”;

http.onrequest(function(req, resp) {
if (req.body == “”){
gethtmlresponse();
resp.send(200, htmlresp);//send this if just site was accessed and no requests

}

});

function gethtmlresponse(){

//Google Line Chart
local T1Data = blob(644);
local T2Data = blob(644);
getStreamData(FEED_ID)
local cnt = 0;

foreach(datapoint in stream1.datapoints) {
T1Data.writen(datapoint.value.tofloat(),‘f’);
cnt++;
//server.log(datapoint.value.tofloat());
}
foreach(datapoint in stream2.datapoints) {
T2Data.writen(datapoint.value.tofloat(),‘f’);
//server.log(datapoint.value.tofloat());
}
server.log("Number of Data Points: " + cnt);
T1Data.seek(0,‘b’);
T2Data.seek(0,‘b’);

htmlresp = “\r
”;
htmlresp += “\r
”;
htmlresp += “\r
”;
//from google chart example for line chart
htmlresp += “<script type=“text/javascript” src=“https://www.google.com/jsapi”>\r
”;
htmlresp += “<script type=“text/javascript”>\r
”;
htmlresp += “google.load(“visualization”, “1”, {packages:[“corechart”]});\r
”;
htmlresp += “google.setOnLoadCallback(drawChart);\r
”;
htmlresp += “function drawChart() {\r
”;
htmlresp += “var data = google.visualization.arrayToDataTable([\r
”;
htmlresp += “[‘Count’, '” + Stream1Name + “’, '” + Stream2Name + “’],\r
”;
local dt = 6.0;
local dtm = “”;
local t = 0;
for (t = 0; t < cnt; t++){
try{
htmlresp += “[’” + (cnt - t).tostring() + "’, " + T1Data.readn(‘f’) + ", " + T2Data.readn(‘f’) + “],\r
”;
}
catch(e){
break;
}
}
htmlresp += “]);\r
”;

//Google Gauge
htmlresp += “var options = {\r
”;
//htmlresp += “title: ‘Graph Title’,curveType: ‘function’,legend: { position: ‘bottom’ }};\r
”;
htmlresp += “title: ‘Graph Title’,”;
htmlresp += “curveType: ‘function’,”;
htmlresp += “};\r
”;
htmlresp += “var chart = new google.visualization.LineChart(document.getElementById(‘chart_div’));\r
”;
htmlresp += “chart.draw(data, options);\r
”;
htmlresp += “}\r
”;
htmlresp += “\r
”;

htmlresp += “\r
”;
htmlresp += “\r
”;

htmlresp += “\r
”;
htmlresp += “\r
”;
//display google chart with 2 steams on 1 chart
htmlresp += “<div id=“chart_div”>
\r

htmlresp += “<table style=“margin-left:60px;”>

”;
htmlresp += “<td style=“border-style:none;margin-left:20px” id=‘chart_div1’>”;
htmlresp += “<td style=“border-style:none;margin-left:20px” id=‘chart_div2’>”;
htmlresp += “
”;

//get Xively png chart for each stream if you want
//htmlresp += “<img src=“https://api.xively.com/v2/feeds/” + FEED_ID + “/datastreams/” + Stream1Name + “.png?c=2188c5&g=true&t=%22” + Stream1Name + “%22&b=true&scale=manual&min=-25&max=100&h=200”>
”;
//htmlresp += “<img src=“https://api.xively.com/v2/feeds/” + FEED_ID + “/datastreams/” + Stream2Name + “.png?c=FFCC33&g=true&t=%22” + Stream2Name + “%22&b=true&scale=manual&min=-25&max=100&h=200”>
”;

htmlresp += “\r
”;
htmlresp += “\r
”;

}

function getStreamData(feedID){

local trigger_url = "https://api.xively.com/v2/feeds/" + FEED_ID + ".json?duration=5days&interval=300&limit=1000";
local req = http.get(trigger_url, {"X-ApiKey":API_KEY, "User-Agent":"xively-Imp-Lib/1.0"});     //add headers
local res = req.sendsync();         //send request
//server.log(res.body);
local data = http.jsondecode(res.body);
local dataStreams = data.datastreams;


foreach(s in dataStreams) {
    if (s.id == Stream1Name) {
        server.log("Xively Stream: " + s.id + " found.");
        stream1 = s;
    }
    if (s.id == Stream2Name) {
        server.log("Xively Stream: " + s.id + " found.");
        stream2 = s;
    }
}

return;

};`