webHooks...am I doing something stupid?

I’ve created my first webHook which is showing “healthy” after sending data to my web server, however the data are always null when they arrive.

I have a very basic test set up (MVC .Net), in that I’ve copied the Json data (from one of the device uplinks) and created classes in the same pattern as the data (using VS2017 > Edit > Paste Special > PasteJSonAsClasses) and then using those classes, extracting a small section of the data to put in an SQL database.

I also copied the full text Json into a web page for testing.

So testing…
b.domain.com/controller/action is the data target
a.domain.com/default.htm sending the data via a browser
This works fine and I can see the OPTIONS preflight (which is null) and then the POST (with data), then the database gets updated with both events) and a 200 response is issued.

However when I try the following…
b.domain.com/controller/action is the data target
TTN.com sends the data
This works fine, database gets updated as if it was a preflight (null) and a 200 response is issued.

The b.domain.com server logs only show a POST from TTN server 63.34.43.96 (or another IP which seems to be header dependant) with a 200 response.

Given that everything is the same except the send location and I can see no errors anywhere, can anyone suggest what I might be missing?

My gut instinct is that this is a headers issue but as far as I’m aware they are the same. I’ve checked all my configs and I’m basically “legs akimbo” ( i.e. all methods from anywhere) until I get this sorted.

Is there any place I can view the actual headers + body sent from TTN?

That’s rather down to the language you are using and we don’t see much .Net in these parts.

If you search the forum there are suggestions for sending webhooks to a site that can show you what’s sent plus some blokes PHP. But basically the headers aren’t all that and the JSON is in the body as text, so you need to read the body of the request.

You may find this site useful PTS - V2

It will show all the headers and data that is sent by TTN

Take care not to send any sensitive data and good practice to clean the dumps as soon as you can

Thanks to you both. I’ll spend the morning in the toilet.

Regarding headers, I checked yesterday and I’m receiving all that ok. I can add my own and they appear in the server logs… I’m taking the data from the body using the FromBody attribute and pumping the Json directly into a class (and sub classes) made up from the json structure shown on TTN for that device. My server configs, as I said yesterday, are wide open and everything works from a browser POST. I’ll keep digging…and pooing.

Well, I’ve nearly driven myself insane over this (and my gf isn’t speaking to me either) and still don’t have a solution.
I can’t get the toilets to work (new or the TTN one).
POST just keeps giving me a 500 response and says it can’t save the dump.

My problem is this…
From a browser (with a bit o’ javaScript), the json data (based on the TTN payload screen) goes to the web server (IIS 10), gets bound to the model (built on the TTN json screen structure) therein, quick query of the payload data and plops the result in a DB…Works great. No CORS issues, nothing.

Same thing from TTN and I get nothing and no error.
After hacking about, I can replicate the problem.
So either the problem is that (using the json Content-Type) the preflight (OPTIONS) works but the actual TTN POST gets forgotten about or…
The json format (as sent from TTN) is slightly different from that shown on the console screen, so the model binding doesn’t work.

I can copy this hoo-ha to get an empty json object or string or etc by trying using a header that doesn’t cause a preflight (OPTIONS)…

All I can see from both sources in the logs is 200 and everything tickety-boo but nothing (null, nada) for the json data.

Thing is, this must be bread and butter to you guys, so anyone got a bit of php that can get me over this hump?

The current world class expert of total dudeness recommends this:

All the commentary above involving preflight is a red-herring big enough to feed a family of five, the webhook does a POST, it arrives, you process it, you send back a 200 (aka :+1:) and it’s all good. The return value is optional.

I can ask him to play with your code if you happen to share it, totally optional, we promise not to laugh but it helps about 100,000% if we can see the code, maybe more, certainly not less. He’s a MCP, so he’s not totally anti-M$ and may be able to figure out some pointer.

OK…got this working. I had to “old school” it to get the data. The main problem was that the data as sent looks nothing like the data that I thought was being sent.

Code wise (and this is just a rough out) the action was…

public ActionResult Story([FromBody] jSonFloodVM sid)
{
if (sid.name!=null) {
string payload = sid.data.uplink_message.decoded_payload.PayloadData;
insertStuff.insertData(payload);
}else { insertStuff.insertData(“NULL OBJECT”); }
return new HttpStatusCodeResult(200);
}

where jSonFloodVM is (as grabbed from the payload side panel on the TTN console)…

public class jSonFloodVM
{
        public string name { get; set; }
        public string time { get; set; }
        public Identifier[] identifiers { get; set; }
        public Data data { get; set; }
        public string[] correlation_ids { get; set; }
        public string origin { get; set; }
        public Context context { get; set; }
        public Visibility visibility { get; set; }
        public string unique_id { get; set; }
    }

    public class Data
    {
        public string type { get; set; }
        public End_Device_Ids end_device_ids { get; set; }
        public string[] correlation_ids { get; set; }
        public string received_at { get; set; }
        public Uplink_Message uplink_message { get; set; }
    }

    public class End_Device_Ids
    {
        public string device_id { get; set; }
        public Application_Ids application_ids { get; set; }
        public string dev_eui { get; set; }
        public string join_eui { get; set; }
        public string dev_addr { get; set; }
    }

    public class Application_Ids
    {
        public string application_id { get; set; }
    }

    public class Uplink_Message
    {
        public string session_key_id { get; set; }
        public int f_port { get; set; }
        public int f_cnt { get; set; }
        public string frm_payload { get; set; }
        public Decoded_Payload decoded_payload { get; set; }
        public Rx_Metadata[] rx_metadata { get; set; }
        public Settings settings { get; set; }
        public string received_at { get; set; }
        public bool confirmed { get; set; }
        public string consumed_airtime { get; set; }
        public Version_Ids version_ids { get; set; }
        public Network_Ids network_ids { get; set; }
    }

    public class Decoded_Payload
    {
        public float BAT_V { get; set; }
        public int LAST_WATER_LEAK_DURATION { get; set; }
        public int MOD { get; set; }
        public int WATER_LEAK_STATUS { get; set; }
        public int WATER_LEAK_TIMES { get; set; }

    public string PayloadData
    {
        get
        {
            return "Battery = " + BAT_V.ToString() + " Duration = " + LAST_WATER_LEAK_DURATION.ToString() + " Status = " + WATER_LEAK_STATUS.ToString() + " Time = " + WATER_LEAK_TIMES.ToString();
        }
    }

}

    public class Settings
    {
        public Data_Rate data_rate { get; set; }
        public string coding_rate { get; set; }
        public string frequency { get; set; }
        public int timestamp { get; set; }
    }

    public class Data_Rate
    {
        public Lora lora { get; set; }
    }

    public class Lora
    {
        public int bandwidth { get; set; }
        public int spreading_factor { get; set; }
    }

    public class Version_Ids
    {
        public string brand_id { get; set; }
        public string model_id { get; set; }
        public string hardware_version { get; set; }
        public string firmware_version { get; set; }
        public string band_id { get; set; }
    }

    public class Network_Ids
    {
        public string net_id { get; set; }
        public string tenant_id { get; set; }
        public string cluster_id { get; set; }
        public string cluster_address { get; set; }
    }

    public class Rx_Metadata
    {
        public Gateway_Ids gateway_ids { get; set; }
        public int timestamp { get; set; }
        public int rssi { get; set; }
        public int channel_rssi { get; set; }
        public float snr { get; set; }
        public Location location { get; set; }
        public string uplink_token { get; set; }
        public int channel_index { get; set; }
    }

    public class Gateway_Ids
    {
        public string gateway_id { get; set; }
        public string eui { get; set; }
    }

    public class Location
    {
        public float latitude { get; set; }
        public float longitude { get; set; }
        public int altitude { get; set; }
        public string source { get; set; }
    }

    public class Context
    {
        public string tenantid { get; set; }
    }

    public class Visibility
    {
        public string[] rights { get; set; }
    }

    public class Identifier
    {
        public Device_Ids device_ids { get; set; }
    }

    public class Device_Ids
    {
        public string device_id { get; set; }
        public Application_Ids1 application_ids { get; set; }
        public string dev_eui { get; set; }
        public string join_eui { get; set; }
        public string dev_addr { get; set; }
    }

    public class Application_Ids1
    {
        public string application_id { get; set; }
    }

which was never going to work because what was actually being sent was…

public class JsonDataFormatVM
{

    public End_Device_Ids end_device_ids { get; set; }
    public string[] correlation_ids { get; set; }
    public string received_at { get; set; }
    public Uplink_Message uplink_message { get; set; }
}

public class End_Device_Ids
{
    public string device_id { get; set; }
    public Application_Ids application_ids { get; set; }
    public string dev_eui { get; set; }
    public string join_eui { get; set; }
    public string dev_addr { get; set; }
}

public class Application_Ids
{
    public string application_id { get; set; }
}

public class Uplink_Message
{
    public string session_key_id { get; set; }
    public int f_port { get; set; }
    public int f_cnt { get; set; }
    public string frm_payload { get; set; }
    public Decoded_Payload decoded_payload { get; set; }
    public Rx_Metadata[] rx_metadata { get; set; }
    public Settings settings { get; set; }
    public string received_at { get; set; }
    public bool confirmed { get; set; }
    public string consumed_airtime { get; set; }
    public Version_Ids version_ids { get; set; }
    public Network_Ids network_ids { get; set; }
}

public class Decoded_Payload
{
    public float BAT_V { get; set; }
    public int LAST_WATER_LEAK_DURATION { get; set; }
    public int MOD { get; set; }
    public int WATER_LEAK_STATUS { get; set; }
    public int WATER_LEAK_TIMES { get; set; }
}

public class Settings
{
    public Data_Rate data_rate { get; set; }
    public string coding_rate { get; set; }
    public string frequency { get; set; }
    public long timestamp { get; set; }
}

public class Data_Rate
{
    public Lora lora { get; set; }
}

public class Lora
{
    public int bandwidth { get; set; }
    public int spreading_factor { get; set; }
}

public class Version_Ids
{
    public string brand_id { get; set; }
    public string model_id { get; set; }
    public string hardware_version { get; set; }
    public string firmware_version { get; set; }
    public string band_id { get; set; }
}

public class Network_Ids
{
    public string net_id { get; set; }
    public string tenant_id { get; set; }
    public string cluster_id { get; set; }
    public string cluster_address { get; set; }
}

public class Rx_Metadata
{
    public Gateway_Ids gateway_ids { get; set; }
    public long timestamp { get; set; }
    public int rssi { get; set; }
    public int channel_rssi { get; set; }
    public float snr { get; set; }
    public Location location { get; set; }
    public string uplink_token { get; set; }
    public int channel_index { get; set; }
}

public class Gateway_Ids
{
    public string gateway_id { get; set; }
    public string eui { get; set; }
}

public class Location
{
    public float latitude { get; set; }
    public float longitude { get; set; }
    public int altitude { get; set; }
    public string source { get; set; }
}

Going through all the other hoo-ha with OPTIONS etc was me trying to reduce the “screw up” surface and get this down to basics.

In the end I used the multipart/form-data Content-Type so the data would be coming in as a POST to ensure there was no chance of a foul up and grabbed the data as a string.

Now I can get data (knowing what it looks like), I can go back to the fancy way of doing this and get back to what I was doing in the first place which was a fire ALARM (I was using a flood alarm to test with) in a forest ends up in a fire brigade’s Telegram group.

It is well documented and pretty much identical to what you get in the console.

I’m sure you won’t be surprised to be told that LoRaWAN is not a good use case for safety critical systems, but if you are going to use it, why make the code complicated - complicated usually ends up having more subtle corner cases that trip up the system in an unexpected way.

Pretty much identical is not 100% identical which was the problem (in …Net these days). Anyway, now I know for the future. I had to really hunt for that code, because it’s not how it is done these days.

It’s here for anyone else…

The fire alarm thing is not safety critical, more an efficiency improvement. Currently, there are blokes who drive the country lanes in 4x4s with cisterns on the back of the truck looking for small fires that they can put out before they become big fires. If LoRa can help advise that there is a possible fire source then that cuts reaction time.

Next (using LoRa) I’m going to do donkey tracking, then I want to do owl surveys. I’m a farmer BTW, so I got interested in all this for irrigation/water usage, which I’ll get 'round to soon.

Sort of ish, not really - you’ll see comments in the PHP and the Python code that extract values with a mind that the key may not be included - like fcnt and pretty much anything that can be zero.

Anyhoo…I now know for the future, which is the important bit and I can now have fun with all this. It’s still kind of weird, sticking a sensor in a glass of water in a forest, which updates my database via the internet.
Out here in the sticks, we have nothing and city salespeople selling LoRa dreams get run out of town, usually with the words, CLOUD, MESH and SMART rammed up their… If I can come up with a good few working use cases, I reckon I can get a bit of community goodness going before the Tel-com mob hoves into view with their oRAN (price pointed) nonsense.