System design feedback requested

Not knowing a better way to have one model for numerous sites, I implemented the following and would appreciate feedback if there’s a more elegant (aka better) design. At this time, the system I’ve built is only for internal use at numerous sites, but we’re always considering the possibility of commercializing it.

Here’s how it goes:

In the agent at startup, I extract the unique “tail” of the agent’s URL and issue a http GET to my server to get the configuration settings for both the agent and the device.

The PHP code on my server uses the tail that’s passed as the primary key to a table holding all of the required information, such as GroveStreams API key, type of device connected to each port, name of the data stream on each port, etc. This data is packaged up and sent back to the agent in JSON format.

There’s a http handler in agent to receive the message from the server, extract what it needs to post data to GroveStreams and then builds a table that it sends to the device in order for the device to get its configuration. This also allows me to “push” new settings to the agent and/or device if I change any of them in the database.

The device has a handler which extracts the configuration settings it receives from the agent.

When the device starts up, it notices it has no configuration, so it sends a message to the agent to get settings. I guess I could use NV storage, but haven’t implemented this aspect yet. I’m not sure there’s much benefit and what I’m doing ensures I always have the most recent configuration.

So far, this all seems to work swimmingly well. However, since I work in a vacuum (with the benefit of forums such as this), I need a sanity check…as does my code!

Thanks for any constructive feedback!

That seems pretty reasonable; you could use server.load/save (agent side storage) to persist this, but if you’re fine with fetching settings at every boot then there’s really no need.

In a commercial setup, you might well use the agent side storage along with your http handler to store these settings; the app doing the blinkup gets the agent URL when successful, so it can then set up the agent direct from the app.

Thanks so much for the feedback! This gives me some assurance that I’m not doing something that will come back to haunt me. That being said, I have seen some dialogue about the agent’s URL and no guarantee that the last portion of the URL will always be 12 characters. Therefore, I changed my code to that which beardedinventor offered, hoping it will be a more long-term solution. I simply couldn’t see the sense in keying off the entire URL, but that may be short-sighted on my part.

@ctmorrisonhvacsp thanks for the great summary.
What is your database of choice? I am currently using the Drupal platform as my back-end (PHP with MySQL). I use PHP to analyze the data coming from multiple Imps and trigger events based on the results. I am not sure if the PHP/MySQL combination is the best way to go.
I would like to know what the commercial Electric Imp customers use for back-end. Thanks for any feedback!

We’re using GroveStreams (www.grovestreams.com) for our backend cloud service and we’re exceedingly happy with them. I had tried other similar services, but found the GroveStreams team to offer not only a great suite of services, but superior support. The “other” part of our backend is custom written in PHP and MySQL for our database that holds device configurations. So far, this combination has worked very well and is quite flexible.

I am trying to connect to Grovestreams with my IMP but having some problems, do you have any sample code for connecting that you can share ?

Yes, I’m happy to share some code which is really just a hack of the code used to update Xively. I’ll try to post within the next couple of days, assuming I can get some free time at home.

Thanks
i keep getting "bad parameters to HTTP PUT on following URL
local url = “http://grovestreams.com/api/feed?compId=device1&temp=25&volts=12&api_key=xxxxxxxx”;
local request = http.put(url);

I’m guessing you may benefit from including the second and third parameters for this call, even though they’re listed as optional.

Try using the following:
local request = http.put(url, {"Host":"grovestream.com"},"");

I’ve been posting to GroveStreams for months with several Imps without issue and hope to post some generic code later this week.

I have updated my code as below, but still no luck, must be missing something someware but dont know what

`device.on(“updatetemp”, function(temp)
{
local url = “http://grovestreams.com”;
server.log("url is " + url);

 local headers = {"Host":"grovestream.com""content-type":"text/xml"}
 server.log("header is  " + headers);
 
 local body = "/api/feed/?api_key=xxxxxx&compid=device1&temp=25&volts=12";
 server.log( "body is " + body);
 
 local request = http.put(url, headers, body);

// local request = http.put(url, {“Host”:“grovestream.com”}, “api_key=e172a0cd-9bdc-35e7-af8d-59ee4bf3b612&compid=device1&temp=25&volts=12” );
local response = request.sendsync();
return response;
// server.log(response + “grovestreams response”);
// server.log(request + “line 32”);

});`

Quick look you might need a comma in your headers
local headers = {“Host”:“grovestream.com, “content-type”:“text/xml”}

Also you can use tools like http://requestb.in/ change your url in agent code and use Rquestb.in to inspect your http gets/posts/puts

Here’s some code that definitely works – a bit of a hack from my working code. Obviously, a few things need changed for your environment. However, I send the stream data as part of the URL, not the body.

I believe you also have an error in your body string right after “feed”. You should not have /? – just ?

Just pass the stream updates in your device.send() call, such as
`
device.send(“gs”,"&temp=25&volts=12");

`
// This is where we send the GroveStreams updates
const gsURL = “http://grovestreams.com/api/feed?compid=

local fakeIPAddress = “10.0.0.10”;

local gsApiKey = “111111111111111111111111111”;
local compid = “yourCompId”;

function gsHandler(gsString){
local url = gsURL+compid+"&api_key="+gsApiKey+gsString;
server.log(“GS URL=”+url);
local request = http.put(url, {“Host”:“grovestream.com”, “X-Forwarded-For”:fakeIPAddress},"");
local response = request.sendsync();
server.log("HTTP response: "+response.statuscode);
if (response.statuscode==403){
server.log(“403 error detected”)
device.send(“delay403”,11);
}
}

// register the device message handler
device.on(“gs”, gsHandler);
`

Thank you very much I will change my code

By the way, the X-Forwarded-For header is to address issues when there is more than 1 imp reporting through a gateway. This can be removed if that’s not your situation. On the other hand, it doesn’t hurt to have it in the code. I have two other functions that I use to dynamically build a random IP address in my actual production code.

The 403 status code is also for multiple imps at a location and helps address the issue of them sending updates within 10 seconds of one another. I use this code and complementary code on the device to introduce a delay should the two devices end up getting in sync.

Let me know if you still have issues. I’m glad to help. Without forums such as this, I wouldn’t have gotten as far as I have and I now love the EI platform. We’re switching our whole production environment to it.

Thanks a lot with a bit a of playing about it is now working fine. some bits of your code i did not understand but it certainly to get me going, I was not sure what the device send (delay 403, 11) meant, but it might be usefull as we intend to have many Imps eventually after learning how to code them correctly, I am very glad I found the grovestream site after you mentioned it on the forum it seems much better than any other i have found so far.

What you’re seeing is the result of constant learning and refinement. GroveStreams limits how often it will accept an update from a source. Multiple imps behind the same Internet gateway appear to GroveStreams with the same public IP address. If two or more imps get in sync (sending updates at about the same time (within 10 seconds of one another), the first one wins and the second update is rejected. The second update gets a 403 response for the PUT. With the code I posted, when the agent gets the 403 error, it does the device.send to tell the device to go to sleep for 11 seconds, effectively getting the two devices out of sync (more accurately, out of phase). For my application, the devices send updates once per minute.

My first attempt was to introduce the 11 second delay if I got a 403 error. This seemingly worked, but the folks at GroveStreams informed me that if I included the X-Forwarded-For header with a different IP address for each imp, they had changed their code to differentiate based upon that, too. Out of laziness (?), I left in the 403 code - it didn’t hurt anything.

The GroveStreams folks continue to hear of instances of 403 errors and are working to refine their approach for rate limiting. The GroveStreams folks are amazingly interested in working with users to improve their offering and the users’ experience.

By the way, I am not affiliated with GroveStreams in any fashion other than being a customer.

Ok thanks that explains it. I now have 2 imps uploading but i am struggling a little bit with the streams and groups of streams concepts, but i expect it will become clear in time.
I think the grovestreams offering is way ahead of any other I have seen and look forward to using some of the many features.
can I ask what features you are useing on the IMP,

I have not yet used the stream groups for anything other than organizing them. I often group digital and input streams in their own folders just to keep things minimally organized.

With regard to “features”, what I’m doing is pretty straight forward. I use the tail of the agent’s URL in a call to a PHP program (mentioned above) to get the configuration information for the particular agent and device. This has allowed me to have one code base across several sites with different numbers and kinds of inputs/outputs. I’m using the device’s persistent storage to store output status so I can restore an output state through a reboot. Other than that, I’m simply using the devices for sensing temperature with thermistors, reading equipment status with current switches and other on/off conditions, reading humidity with an i2c device and controlling on/off loads with digital outputs. Oh…and I use the agent.send and device.send quite a bit to take advantage of things I think the agent/device can do best.

I don’t know if this answers your question, so please ask more and I’ll try to answer more specifically.