Reading JSON from Electric Imp

After having some fun reading JSON with JavaScript, I also wanted to read values from my Electric Imp, but I ran into a problem which turned out to be very easy to fix, continue reading to find out what it was and how to deal with it.

JSON

First we have the JSON itself. What we are using here is nothing special and should be fairly easy to read with some not too complicated code. So, what do we want to read? We want something like this

{
	"name": "light",
	"result":"value",
	"coreInfo":
	{
		"connected": true,
		"deviceID": "deviceid"
	}
}

With this we are able to tell what name we give the result, in this case “light”, because I am just going to use the light sensor to get some values to work with. Right after name we got result where the values from the light sensor is put.

After result we got coreInfo, this is actually from SparkCore where sensor readings are returned as JSON by default, so to keep my code simple and universal, I decided to just adapt it to my Electric Imp code too. Inside coreInfo we got connected which can show if the Impee is connected or disconnected (this example is not going to use that, but should be easy to add in)(this example now uses that), and last we got deviceID where we return the device id.

Agent code

To return the JSON we want, we use the Agent for the Electric Imp.

jsonData <- { "name": "light", "result": 0, "coreInfo": {"connected": false, "deviceID": 0}};

function requestHandler(request, response)
{
  response.header("Access-Control-Allow-Origin", "*");
  
  jsonData.coreInfo.connected = device.isconnected();
   
  response.send(200, http.jsonencode(jsonData));
  return;
}
http.onrequest(requestHandler);

device.on("updateLight", function(msg)
{
  jsonData.result = msg;
});

device.on("setDeviceID", function(msg)
{
  jsonData.coreInfo.deviceID = msg;
});

In here we got a bit of code to receive the light values from the Impee, and also the device id.

Device code

To send the values to the Agent we only need a few lines of code

function updateLight()
{
  agent.send("updateLight", hardware.lightlevel());
  imp.wakeup(1, updateLight);
}
updateLight();

agent.send("setDeviceID", hardware.getdeviceid());

and this should do the trick.

JavaScript

We now got got the Impee sending values to the Agent, and the agent set up to give the reply we want. What we need next is a bit of JavaScript to read that reply and parse the JSON so we can put it into fields on a web page.

<html>
	<head>
		<meta content="text/html; charset=ISO-8859-1" http-equiv="content-type">

		<script type="application/javascript">
			function loadJSON()
			{
				var data_file = "https://agent.electricimp.com/xxxxxxxx-xxxxx"; //Insert link to your Agent here
				var http_request = new XMLHttpRequest();
				try
				{
					// Opera 8.0+, Firefox, Chrome, Safari
					http_request = new XMLHttpRequest();
				}
				catch (e)
				{
					// Internet Explorer Browsers
					try
					{
						http_request = new ActiveXObject("Msxml2.XMLHTTP");
					}
					catch (e)
					{
						try
						{
							http_request = new ActiveXObject("Microsoft.XMLHTTP");
						}
						catch (e)
						{
							alert("Something went wrong. :/");
							return false;
						}
					}
				}

				http_request.onreadystatechange = function()
				{
					if (http_request.readyState == 4)
					{
						if (http_request.status == 200)
						{
							var jsonObj = JSON.parse(http_request.responseText);

							if(jsonObj.coreInfo.connected)
							{
								document.getElementById("device").innerHTML =  jsonObj.coreInfo.deviceID;
								document.getElementById("function").innerHTML =  jsonObj.name;
								document.getElementById("result").innerHTML = Math.round((jsonObj.result/65535) * 100) + "%";
							}
							else
							{
								document.getElementById("device").innerHTML =  "offline";
								document.getElementById("function").innerHTML =  "&nbsp;";
								document.getElementById("result").innerHTML = "&nbsp;";
							}
						}

						setTimeout("loadJSON()", 1000);
					}
				}

				http_request.open("GET", data_file, true);
				http_request.send();
			}

			window.onload=function()
			{
				loadJSON();
			};
		</script>

		<style>
			#devices
			{
				font-family:"Trebuchet MS", Arial, Helvetica, sans-serif;
				width:700px;
				border-collapse:collapse;
			}

			#devices td, #devices th
			{
				font-size:1.2em;
				border:1px solid #98bf21;
				padding:3px 7px 2px 7px;
			}

			#devices th
			{
				font-size:1.4em;
				text-align:left;
				padding-top:5px;
				padding-bottom:4px;
				background-color:#A7C942;
				color:#fff;
			}
		</style>

		<title>Electric IMP and JSON</title>
	</head>

	<body>
		<h1>Electric IMP and JSON</h1>

		<table id="devices" class="devices">
			<tr>
				<th width="300px">Device</th>
				<th width="200px">Function</th>
				<th>Result</th>
			</tr>
			<tr>
				<td>
					<div id="device">&nbsp;</div>
				</td>
				<td>
					<div id="function">&nbsp;</div>
				</td>
				<td>
					<div id="result">&nbsp;</div>
				</td>
			</tr>
		</table>
	</body>
</html>

And this should do just that…

However, as mentioned in the beginning of this post, I had a problem reading from the Electric Imp agent. With the exact same code I could read from the SparkCore API just fine, so I started digging a bit. The browser console I got “No ‘Access-Control-Allow-Origin’ header is present on the requested resource.”, and it turned out that I had to send an extra header when sending the reply from the agent, to be able to use it on other pages.

To fix this I simply had to set

response.header("Access-Control-Allow-Origin", "*");

Just before sending the reply from the Electric Imp Agent. A little simple problem, if you know what is wrong. 🙂

Warning!!!

Yes, a bit dramatic headline, but better safe than sorry… Using this code will expose your agent url to whoever access the page, so if you use it for other things you might want to add some kind of password/token system for that part, find a way to pass the return to the page without using the direct url, or simply not use this at all.

Leave a Reply