[Solved] Spark Publish, NodeJS and EventSource


#1

Having some trouble getting this all working. I can see data packets flying around with Wireshark, but the Node EventSource library I’m using doesn’t seem to be picking up the cloud events. Any Node people out there interested in taking a look? (I’ll be changing my access key soon, don’t worry)

The web interface works great, but for some reason I can’t get events to show up in Node. I’m testing on CentOS and Win8.

I see there may be a relevant thread over here. Any thoughts as to why Node Eventsource isn’t working?


iOS subscribing to published event's by the Spark Core?
#2

Got it figgered.

Doesn’t work:

var EventSource = require('eventsource');
var deviceID = "xxxxxxxxxxxxxxxxxxxx";
var accessToken = "xxxxxxxxxxxxxxxxxxxxxxxxxxx";

var url = "https://api.spark.io/v1/devices/"+deviceID+"/events/?access_token="+accessToken;

console.log("Listening on "+url+" ...");

var es = new EventSource(url);

es.onmessage = function(e){
  console.log( e.data);
};

es.onerror = function(){
  console.log('ES Error');
};

Does work:

var EventSource = require('eventsource');
var esInitDict = {rejectUnauthorized: false};

var deviceID = "xxxxxxxxxxxxxxxxxx";
var accessToken = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";

var url = "https://api.spark.io/v1/devices/"+deviceID+"/events/?access_token="+accessToken;

console.log("Listening on "+url+" ...");

var es = new EventSource(url);

es.addEventListener('Environment', function(e){
	console.log( e.data);
}, false);

The examples I was referencing were using the es.onmessage method, which don’t get thrown for some reason. Using es.addEventListener totally works. Bam!


The SSE(subscribe a event in Browser) seems not support CORS?
#3

I am having the same issue Trying to use EventSource node.js lib but even with your tip it does not work. My code is here:

var EventSource = require('eventsource');

var es = new EventSource('https://api.spark.io/v1/devices/xxx/events/myevent?access_token=xxx');
//var es = new EventSource('http://demo-eventsource.rhcloud.com/')

es.addEventListener('Environment', function(e){
	console.log( e.data);
}, false);

The strange thing: it works with the other eventsource - demo-eventsource that is commented out above.

Why would one listen to the “Environment” Event ? Very strange naming…

One other difference is that I am trying to get the myevent specifically. I’ve tried it all out in curl, works nicely there…

Any ideas?


#4

Hi @hansamann

I think your problem is a name mismatch. The listener you add must match the event name and from the URL you are using, the event name is “myevent” by the listener is listening for “Environment”. You need to either remove myevent and listen for all events from that device or make them the same. I would drop “myevent” from the URL and just listen for the event name you are publishing from your core.

In any case, these names need to match what are you using in Spark.publish("Environment",...) or "myevent" as you want.

For @emc2: The onmessage function listens for an event named “message” which is the default event that browsers use. If you make your core do Spark.publish("message", ...) then you can use that, but there is no need to do so.

See here for the Javascript version:


#5

HI @bko,

yes I noticed that the event was named “Environment” - so I rennamed it to
es.addEventListener(‘myevent’, function(event) {
console.log(event);
});

for the Publish(‘myevent’, …) in my case. I also removed the event name from the URL, e.g. I listen to all events of that device. Problem still is that I only get “empty” events, e.g. {} - but every 5 seconds which is my current publish interval.

It’s great that it seems to work in a browser, but due to security (access token plain text in the js code) I believe no real-world use case could use it that way. That’s why I wanted to use it server-side, I can then make sure the access token does not get exposed.


#6

Hi @hansamann

I see your myevent’s going by in the public event stream and they look good. I can access them with the Javascript version:

MessageEvent {ports: Array[0], data: "{"data":"{\"Hours\": 0, \"Minutes\": 39, \"Seconds…:13:08.557Z","coreid":"48ff72065067555019252287"}", source: null, lastEventId: "", origin: "https://api.spark.io"…}

The browser-based Javascript code is meant to show you how it works, not be a complete solution. That said, I do use something similar by putting the file in the private part of my Dropbox account. That way I can launch a browser on my home computer or on my iOS device to watch my events. I totally agree and even warn you in the example not to put this out in public.

I think the plan is to allow for more use of the Authorization Bearer: pre-header to reduce public exposure. The new web-hooks feature that is coming will also open up more doors, since everything happens on the Spark cloud server.


#7

HI @bko, thx a lot. I’ve got a motion sensor demo up and running. It’s just turning the screen red whenever a motion was detected, works very nicely. Still my goal with this is in the end to get the EventSource working from a server. If you come across anything that works on node.js, please let me know.


#8

Hi @hansamann

Glad you have a baseline demo working!

Did you see @Dave 's message in the other thread about this SSE package? Are you already using this?

https://www.npmjs.org/package/sse

I am sorry I am not a node.js guy so I don’t have much to add here.


#9

So the sse lib mentioned is really a lib to send out these events. The client code mentioned is either browser code or uses the node.js eventsource library, which does not seem to work correctly.


#10

@emc2 could you do me a favor and check if eventsource still works for you? All I can get are empty events. Which version of eventsorurce were you using?


#11

@hansamann Yep, still pumping out data!

Demo: http://emc2innovation.com/spark_publish.php (view source, plz don’t steal my access_key :wink: )

Node EventSource script: https://github.com/aslakhellesoy/eventsource-node but NOT with the mentioned es.onmessage() event!

If you want, PM me your device ID, access_key and event name and I’ll test it out over here.


#12

Hi @emc2, thx a lot. Sorry for that long time between your posting. so just to make it 100% sure: you used teh EventSource out of a browser :slight_smile: That explains it, that does work for me, too. But what I wanted to do was use EventSource in a node.js program which would be the receiver of SSE.


#13

@hansamann - I got EventSource running in a Node.js worker today. I’ll throw a quick rundown past you and feel free to ask for clarification!

For reference, I am using the free hosting on Heroku as a QA space and using foreman start to test locally. I follow this tutorial to get up and running on Heroku.

  1. npm installed: express, logfmt, eventsource (those are the names for "npm install ")

  2. Loaded the Spark with a basic Uptime publisher:

     // publishjson.ino -- Spark Publishing Example 
     void publishUpdate(void);
     unsigned long lastTime = 0UL;
     char publishString[64];
    
     void setup() {
     
         attachInterrupt(D0, publishUpdate, CHANGE);
     }
    
     void loop() {
         unsigned long now = millis();
        //Every 15 seconds publish uptime
         if (now-lastTime>15000UL) {
         lastTime = now;
         // now is in milliseconds
         unsigned nowSec = now/1000UL;
         unsigned sec = nowSec%60;
         unsigned min = (nowSec%3600)/60;
         unsigned hours = (nowSec%86400)/3600;
    
             sprintf(publishString,"{\"Hours\": %u, \"Minutes\": %u, \"Seconds\": %u}",hours,min,sec);
             Spark.publish("Uptime",publishString);
         }
     }
    
     void publishUpdate(){        
         Spark.publish("Uptime", "Forced Publish");
     }
    
  3. Created node app, main file “web.js”:

     var express = require("express");
     var logfmt = require("logfmt");
     var app = express();
    
     var EventSource = require('eventsource');
     var esInitDict = {rejectUnauthorized: false};
    
     var deviceID = process.env.deviceID;	// string, your device ID
     var accessToken = process.env.accessToken; // string, your access token
    
     // Spark URL - must use es.addEventListener and specify the event name
     var url = "https://api.spark.io/v1/devices/"+deviceID+"/events/?access_token="+accessToken;
    
     // Test URL - uses es.onmessage to capture events
     //var url = 'https://demo-eventsource.rhcloud.com/';
    
     /*===================================
     =            EventSource            =
     ===================================*/
     var es = new EventSource(url);
    
     // Only fires for Spark URL
     es.addEventListener('Uptime', function(e){
     	console.log( 'listener: ', JSON.parse(e.data) );
     }, false);
    
     // Only fires for Test URL
     es.onmessage = function(e){
       console.log( 'onmessage: ', e.data);
     };
    
     es.onerror = function(){
       console.log('ES Error');
     };
    
     /*================================
     =            Core App            =
     ================================*/
    
     app.use(logfmt.requestLogger());
    
     app.get('/', function(req, res) {
       res.send('Hello World!');
     });
    
     var port = Number(process.env.PORT || 5000);
     app.listen(port, function() {
       console.log("Listening on " + port);
     });
    
  4. Run with foreman start to test locally. Will only use process.env variables if you have established them in your Heroku app - you may need to swap these out in the definitions with your own hard-coded tokens. foreman start in your console will spit out a few intro debug lines, and then you should start seeing Uptime events every 15 seconds from your Spark

  5. git push heroku master after committing to upload to your QA app, then after it compiles run heroku logs --tail to watch the console.log events roll out. Once again, you should be able to see your Uptime events here.

  6. Have a beer!