Handle webhook chunks

Hi all!

I do my daily commute by train and bus between home and office. And since I will spend every minute available in bed my morning routine is VERY efficient with no room for errors or else I will miss my train.

The trains are not that reliable in the Netherlands and its not strange for me to rush towards the train station to come to the conclusion that the train is delayed or is not coming at all.

So my though, lets crack open an IKEA battery powered lamp, replace the LEDs with NeoPixels, add a dot-matrix and create myself a notification light in the hall way telling me if my train is on time or not and if not how many minutes delay.

So I created a webhook which calls the API of the ā€œNederlandse Spoorwegenā€ (Dutch railways) who in return response with departure times of trains on my station.

But the webhook response is split into multiple chunks of 512 bytes. See;
hook-response/ns-api-avt/0 - till - hook-response/ns-api-avt/7

    {"name":"ns-api-avt","data":"{ \"station_name\": \"hmh\" }","ttl":"60","published_at":"2015-10-31T22:35:54.661Z","coreid":"310029001047343339383037"}
{"name":"hook-sent/ns-api-avt","data":"undefined","ttl":"60","published_at":"2015-10-31T22:35:54.664Z","coreid":"particle-internal"}
{"name":"hook-response/ns-api-avt/0","data":"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n \n\n<ActueleVertrekTijden>\n\n\t<VertrekkendeTrein>\n\t\t<RitNummer>9688</RitNummer>\n\t\t<VertrekTijd>2015-11-01T00:04:00+0100</VertrekTijd>\n\t\t\n\t\t\n\t\t<EindBestemming>s-Hertogenbosch</EindBestemming>\
n\t\t<TreinSoort>Sprinter</TreinSoort>\n\t\t\n\t\t\t<RouteTekst>Eindhoven, Boxtel</RouteTekst>\n\t\t\n\t\t\n\t\t\t<Vervoerder>NS</Vervoerder>\n\t\t\n\t\t<VertrekSpoor wijziging=\"false\">1</VertrekSpoor>\n\t\t\n\t\t\n\t</VertrekkendeTrein>\n\n\t<VertrekkendeTrein>\n\t\t<RitNummer>9687</RitNummer>\n\
t\t<VertrekTijd>2015-11-01T0","ttl":"60","published_at":"2015-10-31T22:35:55.171Z","coreid":"particle-internal"}
{"name":"hook-response/ns-api-avt/1","data":"0:26:00+0100</VertrekTijd>\n\t\t\n\t\t\n\t\t<EindBestemming>Deurne</EindBestemming>\n\t\t<TreinSoort>Sprinter</TreinSoort>\n\t\t\n\t\t\t<RouteTekst>Helmond</RouteTekst>\n\t\t\n\t\t\n\t\t\t<Vervoerder>NS</Vervoerder>\n\t\t\n\t\t<VertrekSpoor wijziging=\"fa
lse\">2</VertrekSpoor>\n\t\t\n\t\t\n\t</VertrekkendeTrein>\n\n\t<VertrekkendeTrein>\n\t\t<RitNummer>9619</RitNummer>\n\t\t<VertrekTijd>2015-11-01T07:26:00+0100</VertrekTijd>\n\t\t\n\t\t\n\t\t<EindBestemming>Deurne</EindBestemming>\n\t\t<TreinSoort>Sprinter</TreinSoort>\n\t\t\n\t\t\t<RouteTekst>Helmo
nd</RouteTekst>\n\t\t\n\t\t\n\t\t\t<Verv","ttl":"60","published_at":"2015-10-31T22:35:55.426Z","coreid":"particle-internal"}
{"name":"hook-response/ns-api-avt/2","data":"oerder>NS</Vervoerder>\n\t\t\n\t\t<VertrekSpoor wijziging=\"false\">2</VertrekSpoor>\n\t\t\n\t\t\n\t</VertrekkendeTrein>\n\n\t<VertrekkendeTrein>\n\t\t<RitNummer>9624</RitNummer>\n\t\t<VertrekTijd>2015-11-01T08:04:00+0100</VertrekTijd>\n\t\t\n\t\t\n\t\t<E
indBestemming>Nijmegen</EindBestemming>\n\t\t<TreinSoort>Sprinter</TreinSoort>\n\t\t\n\t\t\t<RouteTekst>Eindhoven, Boxtel, s-Hertogenbosch</RouteTekst>\n\t\t\n\t\t\n\t\t\t<Vervoerder>NS</Vervoerder>\n\t\t\n\t\t<VertrekSpoor wijziging=\"false\">1</VertrekSpoor>\n\t\t\n\t\t\n\t</VertrekkendeTrein>\n\n
\t<VertrekkendeTrein>\n\t\t<RitNu","ttl":"60","published_at":"2015-10-31T22:35:55.676Z","coreid":"particle-internal"}
{"name":"hook-response/ns-api-avt/3","data":"mmer>9623</RitNummer>\n\t\t<VertrekTijd>2015-11-01T08:26:00+0100</VertrekTijd>\n\t\t\n\t\t\n\t\t<EindBestemming>Deurne</EindBestemming>\n\t\t<TreinSoort>Sprinter</TreinSoort>\n\t\t\n\t\t\t<RouteTekst>Helmond</RouteTekst>\n\t\t\n\t\t\n\t\t\t<Vervoerder>NS<
/Vervoerder>\n\t\t\n\t\t<VertrekSpoor wijziging=\"false\">2</VertrekSpoor>\n\t\t\n\t\t\n\t</VertrekkendeTrein>\n\n\t<VertrekkendeTrein>\n\t\t<RitNummer>9628</RitNummer>\n\t\t<VertrekTijd>2015-11-01T09:04:00+0100</VertrekTijd>\n\t\t\n\t\t\n\t\t<EindBestemming>Nijmegen</EindBestemming>\n\t\t<TreinSoor
t>Sprinter</TreinSoort>\n\t\t","ttl":"60","published_at":"2015-10-31T22:35:55.928Z","coreid":"particle-internal"}
{"name":"hook-response/ns-api-avt/4","data":"\n\t\t\t<RouteTekst>Eindhoven, Boxtel, s-Hertogenbosch</RouteTekst>\n\t\t\n\t\t\n\t\t\t<Vervoerder>NS</Vervoerder>\n\t\t\n\t\t<VertrekSpoor wijziging=\"false\">1</VertrekSpoor>\n\t\t\n\t\t\n\t</VertrekkendeTrein>\n\n\t<VertrekkendeTrein>\n\t\t<RitNummer>9
627</RitNummer>\n\t\t<VertrekTijd>2015-11-01T09:26:00+0100</VertrekTijd>\n\t\t\n\t\t\n\t\t<EindBestemming>Deurne</EindBestemming>\n\t\t<TreinSoort>Sprinter</TreinSoort>\n\t\t\n\t\t\t<RouteTekst>Helmond</RouteTekst>\n\t\t\n\t\t\n\t\t\t<Vervoerder>NS</Vervoerder>\n\t\t\n\t\t<VertrekSpoor wijziging=\"f
alse\">2</VertrekSpoor>\n\t\t\n\t\t\n\t</","ttl":"60","published_at":"2015-10-31T22:35:56.179Z","coreid":"particle-internal"}
{"name":"hook-response/ns-api-avt/5","data":"VertrekkendeTrein>\n\n\t<VertrekkendeTrein>\n\t\t<RitNummer>9632</RitNummer>\n\t\t<VertrekTijd>2015-11-01T10:04:00+0100</VertrekTijd>\n\t\t\n\t\t\n\t\t<EindBestemming>Nijmegen</EindBestemming>\n\t\t<TreinSoort>Sprinter</TreinSoort>\n\t\t\n\t\t\t<RouteTeks
t>Eindhoven, Boxtel, s-Hertogenbosch</RouteTekst>\n\t\t\n\t\t\n\t\t\t<Vervoerder>NS</Vervoerder>\n\t\t\n\t\t<VertrekSpoor wijziging=\"false\">1</VertrekSpoor>\n\t\t\n\t\t\n\t</VertrekkendeTrein>\n\n\t<VertrekkendeTrein>\n\t\t<RitNummer>9631</RitNummer>\n\t\t<VertrekTijd>2015-11-01T10:26:00+0100</Ver
trekTijd>\n\t\t\n\t\t\n\t\t<E","ttl":"60","published_at":"2015-10-31T22:35:56.430Z","coreid":"particle-internal"}
{"name":"hook-response/ns-api-avt/6","data":"indBestemming>Deurne</EindBestemming>\n\t\t<TreinSoort>Sprinter</TreinSoort>\n\t\t\n\t\t\t<RouteTekst>Helmond</RouteTekst>\n\t\t\n\t\t\n\t\t\t<Vervoerder>NS</Vervoerder>\n\t\t\n\t\t<VertrekSpoor wijziging=\"false\">2</VertrekSpoor>\n\t\t\n\t\t\n\t</Vertre
kkendeTrein>\n\n\t<VertrekkendeTrein>\n\t\t<RitNummer>9636</RitNummer>\n\t\t<VertrekTijd>2015-11-01T11:04:00+0100</VertrekTijd>\n\t\t\n\t\t\n\t\t<EindBestemming>Nijmegen</EindBestemming>\n\t\t<TreinSoort>Sprinter</TreinSoort>\n\t\t\n\t\t\t<RouteTekst>Eindhoven, Boxtel, s-Hertogenbosch</RouteTekst>\n
\t\t\n\t\t\n\t\t\t<Vervoerder>N","ttl":"60","published_at":"2015-10-31T22:35:56.681Z","coreid":"particle-internal"}
{"name":"hook-response/ns-api-avt/7","data":"S</Vervoerder>\n\t\t\n\t\t<VertrekSpoor wijziging=\"false\">1</VertrekSpoor>\n\t\t\n\t\t\n\t</VertrekkendeTrein>\n\n</ActueleVertrekTijden>","ttl":"60","published_at":"2015-10-31T22:35:56.934Z","coreid":"particle-internal"}

Normal text:

    <ActueleVertrekTijden>
        <VertrekkendeTrein>
                <RitNummer>9688</RitNummer>
                <VertrekTijd>2015-11-01T00:04:00+0100</VertrekTijd>
                <EindBestemming>s-Hertogenbosch</EindBestemming>
                <TreinSoort>Sprinter</TreinSoort>
                        <RouteTekst>Eindhoven, Boxtel</RouteTekst>
                        <Vervoerder>NS</Vervoerder>
                <VertrekSpoor wijziging="false">1</VertrekSpoor>
        </VertrekkendeTrein>
        <VertrekkendeTrein>
                <RitNummer>9687</RitNummer>
                <VertrekTijd>2015-11-01T00:26:00+0100</VertrekTijd>
                <EindBestemming>Deurne</EindBestemming>
                <TreinSoort>Sprinter</TreinSoort>
                        <RouteTekst>Helmond</RouteTekst>
                        <Vervoerder>NS</Vervoerder>
                <VertrekSpoor wijziging="false">2</VertrekSpoor>
        </VertrekkendeTrein>
        <VertrekkendeTrein>
                <RitNummer>9619</RitNummer>
                <VertrekTijd>2015-11-01T07:26:00+0100</VertrekTijd>
                <EindBestemming>Deurne</EindBestemming>
                <TreinSoort>Sprinter</TreinSoort>
                        <RouteTekst>Helmond</RouteTekst>
                        <Vervoerder>NS</Vervoerder>
                <VertrekSpoor wijziging="false">2</VertrekSpoor>
        </VertrekkendeTrein>
        <VertrekkendeTrein>
                <RitNummer>9624</RitNummer>
                <VertrekTijd>2015-11-01T08:04:00+0100</VertrekTijd>
                <EindBestemming>Nijmegen</EindBestemming>
                <TreinSoort>Sprinter</TreinSoort>
                        <RouteTekst>Eindhoven, Boxtel, s-Hertogenbosch</RouteTekst>
                        <Vervoerder>NS</Vervoerder>
                <VertrekSpoor wijziging="false">1</VertrekSpoor>
        </VertrekkendeTrein>
        <VertrekkendeTrein>
                <RitNummer>9623</RitNummer>
                <VertrekTijd>2015-11-01T08:26:00+0100</VertrekTijd>
                <EindBestemming>Deurne</EindBestemming>
                <TreinSoort>Sprinter</TreinSoort>
                        <RouteTekst>Helmond</RouteTekst>
                        <Vervoerder>NS</Vervoerder>
                <VertrekSpoor wijziging="false">2</VertrekSpoor>
        </VertrekkendeTrein>
        <VertrekkendeTrein>
                <RitNummer>9628</RitNummer>
                <VertrekTijd>2015-11-01T09:04:00+0100</VertrekTijd>
                <EindBestemming>Nijmegen</EindBestemming>
                <TreinSoort>Sprinter</TreinSoort>
                        <RouteTekst>Eindhoven, Boxtel, s-Hertogenbosch</RouteTekst>
                        <Vervoerder>NS</Vervoerder>
                <VertrekSpoor wijziging="false">1</VertrekSpoor>
        </VertrekkendeTrein>
        <VertrekkendeTrein>
                <RitNummer>9627</RitNummer>
                <VertrekTijd>2015-11-01T09:26:00+0100</VertrekTijd>
                <EindBestemming>Deurne</EindBestemming>
                <TreinSoort>Sprinter</TreinSoort>
                        <RouteTekst>Helmond</RouteTekst>
                        <Vervoerder>NS</Vervoerder>
                <VertrekSpoor wijziging="false">2</VertrekSpoor>
        </VertrekkendeTrein>
        <VertrekkendeTrein>
                <RitNummer>9632</RitNummer>
                <VertrekTijd>2015-11-01T10:04:00+0100</VertrekTijd>
                <EindBestemming>Nijmegen</EindBestemming>
                <TreinSoort>Sprinter</TreinSoort>
                        <RouteTekst>Eindhoven, Boxtel, s-Hertogenbosch</RouteTekst>
                        <Vervoerder>NS</Vervoerder>
                <VertrekSpoor wijziging="false">1</VertrekSpoor>
        </VertrekkendeTrein>
        <VertrekkendeTrein>
                <RitNummer>9631</RitNummer>
                <VertrekTijd>2015-11-01T10:26:00+0100</VertrekTijd>
                <EindBestemming>Deurne</EindBestemming>
                <TreinSoort>Sprinter</TreinSoort>
                        <RouteTekst>Helmond</RouteTekst>
                        <Vervoerder>NS</Vervoerder>
                <VertrekSpoor wijziging="false">2</VertrekSpoor>
        </VertrekkendeTrein>
        <VertrekkendeTrein>
                <RitNummer>9636</RitNummer>
                <VertrekTijd>2015-11-01T11:04:00+0100</VertrekTijd>
                <EindBestemming>Nijmegen</EindBestemming>
                <TreinSoort>Sprinter</TreinSoort>
                        <RouteTekst>Eindhoven, Boxtel, s-Hertogenbosch</RouteTekst>
                        <Vervoerder>NS</Vervoerder>
                <VertrekSpoor wijziging="false">1</VertrekSpoor>
        </VertrekkendeTrein>
</ActueleVertrekTijden>

What is the best way to gather all these chunks to one string? To avoid breaks down if a line happens to be split across response chunks.

Look at using response templatesā€¦they allow you to cut the data sent in response to only what you want.

2 Likes

The documentation doesnā€™t say much about the responseTemplate. I have not yet tested it but looking at the examples it seems to only work for pure json responses? Which this isnā€™t, the response is XML :slight_smile:

EDIT: I have found another (intermediate) provider which delivers the same data from the original XML but into a JSON format. That is much better to handle and very good to implement with responseTemplate.
But still, if XML is usable with responseTemplate Iā€™ll go for that, it than eliminates the intermediate.

1 Like

I have a similar question about receiving a webhook response that is large enough that Particle sends me back two separate webhook responses.

Iā€™m receiving back weather forecast data for the next day forecast, and this gets sent back in 2 different webhook responses.

Since these responses split the message in half so the code that parses the webhook response does not pick up on the item that gets cut where the message breaks in half.

See the example responses below that shows how the message gets split in half.

1st Part of the webhook response:

2nd Part of the webhook response:

Is there a recommended way of dealing with this?

Is it just best to split the responses up into multiple smaller individual webhooks and use the response templates to pull just enough info per message to avoid the response from splitting up into multiple replies?

If the returned data is valid JSON you can use a ā€œmustacheā€ template to separate the wheat from the chaff.

Considerably lightens the load an you may get to get all of your data in a single response.

Are you able to get valid JSON responses?

@BulldogLowell

Iā€™m using a response template to only send the data I want back and it works just fine except for the data point that gets cut in half and split up into two separate webhook responses.

Here is the response template that Iā€™m using. Is this correct?

"responseTemplate": "

{{#forecast}}

<Date>{{forecast.txt_forecast.date}}</Date>

<Icon0>{{forecast.txt_forecast.forecastday.0.icon}}</Icon0>

<Day>{{forecast.txt_forecast.forecastday.0.title}}</Day>

<Forecast>{{forecast.txt_forecast.forecastday.0.fcttext}}</Forecast>


<Icon1>{{forecast.txt_forecast.forecastday.1.icon}}</Icon1>

<Day1>{{forecast.txt_forecast.forecastday.1.title}}</Day1>

<Forecast1>{{forecast.txt_forecast.forecastday.1.fcttext}}</Forecast1>



<Icon2>{{forecast.txt_forecast.forecastday.2.icon}}</Icon2>

<Day2>{{forecast.txt_forecast.forecastday.2.title}}</Day2>

<Forecast2>{{forecast.txt_forecast.forecastday.2.fcttext}}</Forecast2>



<Icon3>{{forecast.txt_forecast.forecastday.3.icon}}</Icon3>

<Day3>{{forecast.txt_forecast.forecastday.3.title}}</Day3>

<Forecast3>{{forecast.txt_forecast.forecastday.3.fcttext}}</Forecast3>



{{/forecast}}",

You can see that the Day is missing from the last group of weather data text because that is where the webhook was split and the remaining text is sent via the 2nd webhook response.

You can see the split here after Day3:

And that ends with /Day3

wow, that is wordyā€¦

<Day>{{forecast.txt_forecast.forecastday.0.title}}</Day>

Have you thought about not bringing in the text for the Day and the dayā€™s night? Just do that programmatically from the array that you parse. (day zero, one, two, etc).

Strip it down to the bare essentialsā€¦

Wordy indeed :smile: No way around that.

No, I would rather just pull it in so I know it's right vs trying write code to figure out what the Days 0,1,2,3 actually are. I'm only going to pull this future forecast data a couple times a day at most so I'm not worried about the extra data consumed.

It's looking like I should just break up the weather forecast into 2 or 3 separate webhooks so I can guarantee the returned data does not get broken up into 2 separate responses which causes the loss of the data variable that gets split.

I'm learning something new everyday :smiley:

can you post the entire return?

Maybe we will see something in there!

Sure:

Return #1:

 `{"data":"\"responseTemplate\": \"\n\n\n<Date>10:49 AM EST</Date>\n\n<Icon0>clear</Icon0>\n\n<Day>Wednesday</Day>\n\n<Forecast>Plentiful sunshine. High 59F. Winds light and variable.</Forecast>\n\n\n<Icon1>nt_clear</Icon1>\n\n<Day1>Wednesday Night</Day1>\n\n<Forecast1>Clear skies. Low 42F. Winds SE at 5 to 10 mph.</Forecast1>\n\n\n\n<Icon2>clear</Icon2>\n\n<Day2>Thursday</Day2>\n\n<Forecast2>Sunny, along with a few afternoon clouds. High around 70F. Winds S at 15 to 25 mph.</Forecast2>\n\n\n\n<Icon3>nt_partlycloudy</Icon3>`\n\n<Day3>ThursdayNight</D","ttl":"60","published_at":"2016-11-16T16:50:37.236Z","coreid":"particle-internal","name":"2f003a000e51353338363333_Forecast2Day/0"}

The rest, Response #2 :

{"data":"ay3>\n\n<Forecast3>Partly cloudy skies. Low 53F. Winds S at 10 to 20 mph.</Forecast3>\n\n\n\n\",","ttl":"60","published_at":"2016-11-16T16:50:37.495Z","coreid":"particle-internal","name":"2f003a000e51353338363333_Forecast2Day/1"}

You can see the message getā€™s cut on Day3 so the data between that and /Day3 does not get parsed by my code because its returned broken between 2 separate webhook returns.

The weather conditions and text will always change in length so the message will get cut at different area every time which makes it impossible to know where the split is going to happen each time.

I meant the entire dataset from the URL you are requestingā€¦

Here ya go: http://api.wunderground.com/api/c1a68f78b8712baf/forecast/q/IN/marion.json

you could try a simple template like this:

{{#forecast}}{{#txt_forecast}}{{#forecastday}}{{title}}~{{icon}}~{{fcttext}}~{{/forecastday}}{{/txt_forecast}}{{/forecast}}

and concatenate the two/multiple big strings and parse it using strtok(), iterating over the tildes.

also, maybe nixing the title will get you inside one ā€˜page.ā€™ ?? I guess with the Tolstoy edition of the forecast, you may never know just how big this will be!!!

Iā€™ll try your response template that you provide and see what comes back. Thanks for helping, Iā€™m learning something new every day.

I have no idea on how to concatenate the 2 strings that are generated from the webhook response. Do you have any example code that I could look and study?

Once the data from both strings are merged into a single string I can then parse that string for data separated by tildes. That makes sense but I have no idea how to do this without some sort of example to follow. Have you done this before?

so, the data payload is 512bytes, so if we assume that your web hook always returns in two chunks, you could try like this kind-of-thingā€¦

void parseWeather(const char* event, const char* data)
{
  static char weatherData[1024] = "";
  if(strstr(event, "your_hook_name/0"))
  {
    strcpy(weatherData, "");
    strcpy(weatherData, data);
  }
  else if (strstr(event, "your_hook_name/1"))
  {
    strcat(weatherData, data);  //concatenate the first return with the second
    // tokenize and parse here...
    // strtok(weatherData, "~");
    // etc...
  }
  else
  {
    // error handling here...
  }
}

if it may be only one chunk from time to time, you could use a global C string and a timeout to start the parsing.

Ha! I just read the text under the web handler function code Iā€™ve been using that clearly says it will not work if the data is split up :smile:

@BulldogLowell Your code looks easy enough to understand, it makes sense to me at first glance.

I am wondering what this line accomplishes though:

I just need to change my Response Template, so we get the ~ tildes inserted between the data variables in the webhook response.

Then we can just parse one large string using strtok(weatherData, "~");

Iā€™ve never used strtok to parse before, but this should be pretty easy to figure out via searching the forum.

Iā€™ll let you know once I have it working or if I get stumped along the way :smile:

Thanks!

@BulldogLowell Another quick question while trying to set this up.

my webhook response will have an even name that includes the device ID number so it will only react to webhook responses that it generated. See below:

How should I go about customizing the code below so it will automatically drop in the device ID for the particular device the code is running on?

I assume I can just use this function to put the device ID into a variable

but how would I properly enter that into this part of the code:

Maybe something like this? :

void gotForecast2(const char* event, const char* data)
{
  String myID = System.deviceID();

  static char weatherData[1024] = "";
  if(strstr(event, "(myID)_Forecast2Day/0"))
  {
    strcpy(weatherData, "");
    strcpy(weatherData, data);
  }
  else if (strstr(event, "(myID)_Forecast2Day/1"))
  {
    strcat(weatherData, data);  //concatenate the first return with the second
    // tokenize and parse here...
    // strtok(weatherData, "~");
    // etc...
  }
  else
  {
    // error handling here...
  }
}

OK, a few steps:

I want to respond to every webhook I called, so in setup() I have:

responseTopic = System.deviceID();
Particle.subscribe(responseTopic, webhookHandler, MY_DEVICES);

and then I create a webhook handler function that will sort out any/all of the webhook calls I made:

void webhookHandler(const char *event, const char *data)  // you are here because your webhook contained the deviceID
{
  if (strstr(event, "Forecast2Day"))  // returns true if this substring is in 'event' verbatim
  {
    gotForecast2(event, data);
  }
  else if(strstr(event, "myOtherWebhook")) // handle other webhook
  {

  }
  else if( ... // etcetera
}

I handle the Forecast2day webhook like you have above:

void gotForecast2(const char* event, const char* data)
{
  static char weatherData[1024] = "";
  if(strstr(event, "Forecast2Day/0"))
  {
    strcpy(weatherData, "");  // empty the string
    strcpy(weatherData, data);  // copy the first return to the empty string
  }
  else if (strstr(event, "Forecast2Day/1"))
  {
    strcat(weatherData, data);  //concatenate the first return with the second
    // tokenize and parse here...
    // strtok(weatherData, "~");
    // etc...
  }
  else
  {
    // error handling here...
  }
}

I hope that helpsā€¦

    strcpy(weatherData, "");  // empty the string
    strcpy(weatherData, data);  // copy the first return to the empty string

The first strcpy() is superfluous. Since it doesnā€™t actually empty the string, but only write a \0 to the first byte of weatherData but the subsequent call will overwrite that byte anyhow, even with an empty data response.
If you want to actually clear the array, youā€™d rather want to use memset(weatherData, 0, sizeof(weatherData)); which fills the complete array with \0.

@RWB, since you prepend your device ID to your event name, you can filter for that device ID in the Particle.subscribe() call and the handler will only fire when the event starts with that ID (thatā€™s also how product webhooks are meant to find their way ack to the desired device by prepending the device ID even in front of HookResponse).
If youā€™ll have the device ID embedded in or appended to your event name, youā€™ll have to use strstr() or the likes.

Your comments are superfluous, since I demonstrated exactly that technique in the exemplar. :wink:

webhookHandler(); will respond to any event preceded with the deviceID it then looks to narrow down the candidates of webhooks he is using by using the strstr() method and subsequently calling a webhook specific function for that particular webhook/event.

strcpy(weatherData, "");  // empty the string
strcpy(weatherData, data);  // copy the first return to the empty string

A null terminated string with a null at [0] or in all positions is an empty string for the purposes of C strings. The pseudo code was shown as a way of showing what's happening and no other reason.

Not everyone provides examples the same way.