Subscribing to a hook response

I am trying to catch data from a hook response but my handler function doesn't seem to execute. Do I need more unique event names here?

void setup() {
//get the sleeptime value from the cloud
Particle.subscribe("hook-response/Wake", sleepTimeHandler);
//Publish event to trigger webhook that fetches my value
Particle.publish("Wake", "{"ubi-dsl-vl":"" + Particle.deviceID() + "/" + "sleep-time" + "", "ubi-token":"" + ubidotsToken + ""}", 60, PRIVATE);
}
void loop(){ }

void sleepTimeHandler(const char *event, const char *data){
sleeptime = atoi(data);
Serial.printlnf("SleepTime: %i", sleeptime);
Serial.printlnf(data);
Serial.printlnf(event);
}

I can see the events and hook responses with my data in the console. Do I need to specify a listener for "Particle Internal?"
Is there a better way to do this?

1 Like

@PopQuiz,

It took me a while but, I found a process that works. Here is how I do it:

  1. When I define the Webhook, I specify the response topic to include the Particle device’s system ID - that way multiple devices can use the same hook and each get their own response
{{PARTICLE_DEVICE_ID}}_Ubidots-Hook
          
  1. I put the system ID in the Webhook subscription in Setup();
  char responseTopic[125];
  String deviceID = System.deviceID();                                // Multiple Electrons share the same hook - keeps things straight
  deviceID.toCharArray(responseTopic,125);
  Particle.subscribe(responseTopic, UbidotsHandler, MY_DEVICES);      // Subscribe to the integration response event
  1. When I created the Webhook, I also specified a response template - in this case to get the response code from Ubidots and pass it on to my device.
{{hourly.0.status_code}}
  1. In my code, I wrote a Response Handler to look at the response code and take appropriate action:
// ****************** Please see updated code later in this thread **************************
void UbidotsHandler(const char *event, const char *data)  // Looks at the response from Ubidots - Will reset Photon if no successful response
{
  // Response Template: "{{hourly.0.status_code}}"
  if (!data) {                                            // First check to see if there is any data
    Particle.publish("Ubidots Hook", "No Data");
    return;
  }
  int responseCode = atoi(data);                          // Response is only a single number thanks to Template
  if ((responseCode == 200) || (responseCode == 201))
  {
    Particle.publish("State","Response Received");
    dataInFlight = false;                                 // Data has been received
  }
  else Particle.publish("Ubidots Hook", data);             // Publish the response code
}

This enables me to share web hooks, direct the responses to individual devices and simplify my code by using the response template to strip out all the fluff.

Hope this helps,

Chip

Very cool way to not fill up your console with webhooks for each device. I have not played with response templates but now I will. Thanks.
By the way, I noticed you have publish calls in a subscription handler which reminded me of this tidbit from the docs. Maybe they fixed this - not sure.

NOTE 2: Particle.publish() and the Particle.subscribe() handler(s) share the same buffer. As such, calling Particle.publish() within a Particle.subscribe() handler will wipe the subscribe buffer! In these cases, copying the subscribe buffer's content to a separate char buffer prior to calling Particle.publish() is recommended.

@PopQuiz,

Thanks for pointing that out. This handler works fine so maybe it is fixed?

Chip

For some reason I still don’t fully understand, just specifying “MY_DEVICES” in the subscription fixed my issue.
I was able to learn something about response handling due to your very helpful post. Thanks.

I don't quite follow ...

What exactly is the puzzle?

You have quoted Note 2 but your actual problem seems more related to the fact that events that are published PRIVATE (and also webhook responses triggered by that) have to be subscribed to with MY_DEVICES and for PUBLIC (currently the default, but that's about to change with 0.8.0) you need to subscribe ALL_DEVICES (currently default).
You can't mix-and-match.

Nope, you just happen to be lucky in this case. I suggest you copy data to a temporary char array and work off of that so if the data buffer gets overwritten, it won't matter.

2 Likes

Well, the hook response comes from a device which isn’t mine, but now I understand it is triggered by mine, so the parameter should be there.
As for Note 2, I merely pointed that out because it was fresh in my mind when I read ChipMC’s post about response handling.

1 Like

@peekay123,

Thank you. I think this code actually does copy the data off since the response template reduces the response from Ubidots to a single number which gets stored in the “responseCode” variable.

Still, for future reference and where I might have a more complex response (like getting weather for example), this is very good to know.

Chip

@chipmc, the code works because the first Publish only happens if there is nothing in data so there is nothing to wipe! Bottom line is if you want to do a Publish while parsing/working with data, you need to copy it to a temporary buffer. :wink:

2 Likes

@peekay123,

In case someone might look at this thread in the future, and for my other response handler uses, I have updated the code. Thank you for pointing out what I needed to do.

void UbidotsHandler(const char *event, const char *data)  // Looks at the response from Ubidots - Will reset Photon if no successful response
{
  // Response Template: "{{hourly.0.status_code}}" so, I should only get a 3 digit number back
  char dataCopy[16];                                    // data needs to be copied since Particle.publish() will clear it
  strncpy(dataCopy, data, sizeof(dataCopy));            // Copy - overflow safe
  if (!dataCopy) {                                      // First check to see if there is any data
    Particle.publish("Ubidots Hook", "No Data");
    return;
  }
  int responseCode = atoi(dataCopy);                    // Response is only a single number thanks to Template
  if ((responseCode == 200) || (responseCode == 201))
  {
    Particle.publish("State","Response Received");
    dataInFlight = false;                                 // Data has been received
  }
  else Particle.publish("Ubidots Hook", dataCopy);       // Publish the response code
}

Chip

2 Likes

One note to “improve” or generalise the handler code would be to not assume the size of the data, but have it dynamic

  char dataCopy[strlen(data)+1];

You may also want to mark the events PRIVATE to prevent them from ending up in the public event stream.

I’m also not sure about if (!dataCopy).
I’d rather go with any of the following if (!strlen(dataCopy)), if (!dataCopy[0]) or if (!*dataCopy) as your version checks whether the the array lives at location 0x00000000 or not - but it never will as it will be allocated on the stack :wink:

1 Like

@ScruffR,

Had not thought of the initialization of dataCopy being sized by data - great idea.

OK, even better now

void UbidotsHandler(const char *event, const char *data)  // Looks at the response from Ubidots - Will reset Photon if no successful response
{
  // Response Template: "{{hourly.0.status_code}}" so, I should only get a 3 digit number back
  char dataCopy[strlen(data)+1];                                    // data needs to be copied since Particle.publish() will clear it
  strncpy(dataCopy, data, sizeof(dataCopy));            // Copy - overflow safe
  if (!strlen(dataCopy)) {                                      // First check to see if there is any data
    Particle.publish("Ubidots Hook", "No Data");
    return;
  }
  int responseCode = atoi(dataCopy);                    // Response is only a single number thanks to Template
  if ((responseCode == 200) || (responseCode == 201))
  {
    Particle.publish("State","Response Received");
    dataInFlight = false;                                 // Data has been received
  }
  else Particle.publish("Ubidots Hook", dataCopy);       // Publish the response code
}

Thanks! Chip

1 Like