GroveStreams

Point stream control from dashboard21

JoeDG private msg quote post Address this user
I'd love to see the ability to add a "control" to a dashboard that would change the value of a point stream.

For example, I have a greenhouse temperature setpoint that is controlled by a point stream. It would be great to display a control on my dashboard that can change the value of this point stream. This would allow the creation of interactive dashboards, where the user can change parameters and see the results.
Post 1 IP   flag post
MikeMills private msg quote post Address this user
We've had several requests for controls. Your suggestion would be fairly easy to implement. So your control would use the GroveStreams API and poll for the point stream value?

If so, it's an easy solution but it would be a lot of wasted transactions given all the polling that would need to be done.

One design we are considering is allowing a control value to be returned during a feed upload. A control would be associated with a component and whenever its value is changed, its new value is part of the Feed PUT response. We haven't thought through the details yet, but it would be nice to leverage the Feed PUT response.
Post 2 IP   flag post
JoeDG private msg quote post Address this user
Correct, I would have to poll the stream value. It obviously could only be used for slowly updating systems, but it's a nice way to be able to change parameters from any location.

Including a control value in the PUT response is a nice way to save transactions. As long as it could be safely ignored by the sender, It seems like a decent way to do it.
Post 3 IP   flag post
jonrichings private msg quote post Address this user
Any news on when any kind of control functionality is to be added? The way you described doing it Mike is exactly what we need, but honestly without it, we can't make use of an otherwise brilliant platform.
Post 4 IP   flag post
MikeMills private msg quote post Address this user
@jonrichings - Which technique is what you need:
* "...your control would use the GroveStreams API and poll for the point stream value"?
* "...allowing a control value to be returned during a feed upload"?
Post 5 IP   flag post
jonrichings private msg quote post Address this user
Control value to be returned during an upload would probably be easiest to implement at both ends and, as you said, avoid needless polling. I'd use the same stream names/ID in the request so any stream could be requested. But I think it only really needs to be the last datapoint of any stream. Then there needs to be some way to add/change that last datapoint at the dashboard, though just manually writing it would work for us for now.
Post 6 IP   flag post
MikeMills private msg quote post Address this user
Working on a reply
Post 7 IP   flag post
MikeMills private msg quote post Address this user
Still working on a response. Forum keeps posting before I'm done...
Post 8 IP   flag post
MikeMills private msg quote post Address this user
It will take some time to put together a decent command framework. We have a lot of ideas, but it's one area we don't want to rush. That being said, if we can find a quick/simple solution to hold people over in the interim we'd do it.

Today, in your gateway or device, you could follow the feed PUT call with a Feed GET call like this:comp/{compId}/stream/{streamId}/last_value. It is an extra transaction, but if your sample frequency isn't high then it isn't a big deal.

The ability to include a query(s) with a Feed PUT might not be that hard. My initial thoughts are this:

We allow a collection of queries to be included with the Feed Put - the JSON Feed PUT, not the URL Feed PUT. So the body would look something like this:
[
  {
    "compId": "comp1",
    "streamId": "wind",
    "data": 2.34
  },
  {
    "compId": "comp1",
    "streamId": "temp",
    "data": 72.3
  },
  {
    "query": "query1",
    "compId": "comp1",
    "streamId": "reset",
    "afterDate": 1422646206
  },
  {
    "query": "query2",
    "compId": "comp1",
    "streamId": "sampleFreq",
    "afterDate": 1422646206
  },
  {
    "query": "query3",
    "compId": "comp1",
    "streamId": "temp",
    "cycleId" : "day",
    "stat" : "max",
    "afterDate": 1422646206
  }
]


Each query only returns the last_value, if the last_value's timestamp is later than afterDate. This way only new values are returned. Your device would have to hold the time of the previous last_value between uploads.

The first two tuples are uploading stream values.
Query1 returns the last_value of the "reset" stream if it changed since the afterDate.
Query2 returns the last_value of the "sampleFreq" stream if it changed since the afterDate.
Query3 returns the daily high for the temp stream. Before or after the current upload's values? I'm guessing after this upload so that the current values are included in the max calculation.

The response body would look like this (Matches what Feed GET returns except we add the query name):
 [{
    "query" : "query1"
    "compId": "comp1",
    "streamId": "reset",
    "data": true,
    "time": 1380225121000
 },
{
    "query" : "query2"
    "compId": "comp1",
    "streamId": "sampleFreq",
    "data": 20,
    "time": 1380225121000
 },
{
    "query" : "query3"
    "compId": "comp1",
    "streamId": "temp",
    "data": 55.5,
    "time": 1380225121000
 }
]


Now, if no new samples have arrived with a timestamp later than "afterDate", then nothing is returned.

We would probably still count this as two transactions for billing purposes as it is adding to the system workload. This is my initial thought and wouldn't be that hard to whip up if someone actually sees a need for it. The command queue idea is probably more scalable....

Feedback anyone?
Post 9 IP   flag post
jonrichings private msg quote post Address this user
Mike, I got your reply in email but it's not showing up in the forum. Anyway, yeah, I think that scheme would work fine and be perectly usable for our needs. But I'd also suggest an even quicker (if dirtier) workaround. We are moving from Exosite (don't ask) and we've been using this structure in outgoing posts:

	Serial1.println("POST /api:v1/stack/alias?XC HTTP/1.1"); // Includes request for data from alias "XC"
	Serial1.println("Host: m2.exosite.com");
	Serial1.print("X-Exosite-CIK: ");
	Serial1.println(CIK);
	Serial1.println("Accept: application/x-www-form-urlencoded; charset=utf-8");
	Serial1.println("Content-Type: application/x-www-form-urlencoded; charset=utf-8");
	Serial1.print("Content-Length: ");
	Serial1.println(strlen(writeString)); //calculate length
	//Serial1.println(sizeof(writeString)); //calculate length
	Serial1.println("");
	Serial1.println(writeString);// Done with the data transmission


You can see that we just add the alias (streamID) to the POST string (in this case it's called XC). The CompID of the StreamID is assumed to be the same as that of the POST. Timing is not an issue in most cases since the returned data is typically status or parameter so it can just be compared to the last response and used or ignored if it hasn't changed. Not at all as sophisticated as your JSON suggestion (which I'd very much like to see implemented) but real quick and would work for most simple parameter changing needs. If we need more than one StreamID then we just keep requesting them (wasteful, but again, this direction is rarely needed, so not a big deal).
Post 10 IP   flag post
MikeMills private msg quote post Address this user
JSON parsing on a device can be tough so your solution might be the drop dead simple solution and my JSON solution can come later.

I think I understand. alias?XC above tells their site to return the last_value of stream XC? I imagine the response body is just the last_value - not tokenized by JSON or anything?

OK. So another simple proposal:
We add a parameter to the PUT feed (No JSON Body) API called rsid (return stream id) and this will return the value of the stream for the component uploading.

Example:
http://grovestreams.com/api/feed?compId=comp1&temp=3&wind=4&rsid=sampleFreq


The last_value for the comp1 stream sampleFreq will be returned in the http body and will be:
20
Post 11 IP   flag post
jonrichings private msg quote post Address this user
That would be perfect for us Mike! I'd still be looking forward to the JSON's version for full versatility but the simple PUT feed fix would work for most of our current needs, and suspect a great many other folk too.
If you implement that, how can we change the last value in a stream at the web end? Manually editing values would work for the short term but that really needs some other mechanism linked to a text box, toggle, dial or something in the dashboard. I see another tab in the add content window coming!
Post 12 IP   flag post
MikeMills private msg quote post Address this user
We coded up the Feed PUT part. It will work as stated above:
http://grovestreams.com/api/feed?compId=comp1&temp=3&wind=4&rsid=sampleFreq

Except.....we had to return the last_value as JSON. Why? Because our Feed PUTs will return JSON if the server recognizes any of the streams involved are missing from the component. This is to give some clients the chance to send stream meta data up in the next upload to assist with the auto creation of streams.

Anyway, response bodies will look like these (humidity is a missing stream):
{"missing":[{"itemType":"stream","itemId":"humidity","componentId":"comp1"}],"rs":null}

If sampleFreq stream has no samples yet, then null is returned:
{"rs":null}

Last value is returned:
{"rs":20}

If the return stream value type is a String then the value will be surrounded with quotes:
{"rs":"open"}

How does this look? We need to see if anyone else has any opinions around this, update the API pages, and update the production site - probably could do this in a couple of days.

Also, you can add a stream value today by double clicking a stream and adding it there and clicking save. Yeah, not an ideal way to control a device. I think we can add a simple dashboard "Stream Updater" widget. It might not be visually fancy at first.
Post 13 IP   flag post
jonrichings private msg quote post Address this user
Excellent Mike and flippin fast! Since it's only a single parameter coming back we can probably search for the string and ignore the fact it's JSON, so that should work. And, of course, it is a step in the right direction toward a fully fledged JSON solution. As soon as you're ready, let me know and I'll try and be as fast as you in testing it!
Post 14 IP   flag post
MikeMills private msg quote post Address this user
Have at it:
* View the details here (Do an F5 on this page to pickup the latest. Search for rsid).

Slight change from above - you can request more than one return stream at a time. Thus, the JSON is now by the return stream's id:

Example Feed PUT:
http://grovestreams.com/api/feed?compId=comp1&temp=3&wind=4&rsid=sampleFreq&rsid=sleep&rsid=status

Returns (note the value types: number, boolean, string):
{"sampleFreq":20,"sleep":true,"status":"enabled"}

Tip: Keep your stream IDs short to conserve bandwidth. This example didn't for the sake of clarity.

You could also include an HTTP header to have the return body gzip compressed. That's a feature for all of our API calls.

Let us know how it works out. We'll investigate a "Stream Updater" widget.
Post 15 IP   flag post
jonrichings private msg quote post Address this user
It's works great Mike. Only tried a few experiments but, so far so good. Thanks a million.
Post 16 IP   flag post
MikeMills private msg quote post Address this user
Do an F5 to pick up this addition. Consider the Stream Feed Form widget to be in Beta. It has been lightly tested.

This widget simply displays each stream's last value and allows users to append on a new stream value with the sample time being the time on your computer (rounded to the second) when you click the button.

The combination of the Stream Feed Form widget and the Feed PUT (all on the URL) request streams parameter will allow your device to pickup commands/settings from a dashboard through a firewall while keeping transaction rates the same.

It's also a great way for users to hand enter values that are needed in derivation formulas such as a farmer entering in livestock mortalities each day (@PigFarmer).

No fancy switches or dials yet; we're starting with basics first.

We still need to clean this up and document it throughout the website with examples, but we wanted some users to help us shake it out before we broadcast it.


Select the Stream Form widget.



* The form on the left had several streams dragged onto it without any widget settings changed.
* The form on the right is the same set of streams with some formatting changes.
* (1) Validation rules can be applied and a marker will indicate when the rule fails and a Submit will not be allowed.
* Any stream can be dragged onto a single form: Point, Regular, Interval.
* Streams can belong to different Components
* The editing control is determined by the stream type: Boolean is a Radial Control. Number is a Number Field. Text is a text field. DateTime is a time picker.
* The Regex property on streams that are Text/String value types, is used for validation. Search the web for Regex and validation.
* Like most of our other widgets, this widget is:
- embeddable (Your API key will need to allow a Feed PUT)
- exportable to a spreadsheet

Here are some sample style settings:
* Label: font-size:20px; font-family: Florence, cursive, Times, serif; font-weight:bold;font-style: italic;line-height: 35px;color:red;

The line-height style is used to help center the label.

* Field: font-size:20px; font-family: Florence, cursive, Times, serif; font-weight:bold;font-style: italic;color:red; background-color: yellow; color:red;background-image: none;

Enjoy!
Post 17 IP   flag post
MikeMills private msg quote post Address this user
We will be adding an extra security check when you include the rsid parameters to bring back feed data.

Your API key will need to include the capability for a Feed GET. It should look like this:
component/*/feed GET PUT
component PUT
component_template GET
Post 18 IP   flag post
2968 18 18
Log in or sign up to compose a reply.