Google Geolocation API: Help setting up JSON PUSH with Webhooks

I’m pretty new to using webhooks, and I’m trying to send a JSON query to the Google Geolocation API. What I’ve done so far is make some code that scans for wifi and formats the the data as specified via the API. If i do a JSON request from my command line, it comes out fine. Here’s the JSON file I send:

 {
 "considerIp": "false",
 "wifiAccessPoints": [
     {"macAddress": "00:11:22:33:44:55",
      "signalStrength": -70,"channel": 1},
     {"macAddress": "00:AA:BB:CC:DD:FF",
      "signalStrength": -75,"channel": 11}
  ]}

Using the call from the terminal:

  $ curl -d @sample.json -H "Content-Type: application/json" -i "https://www.googleapis.com/geolocation/v1/geolocate?key=APIKEY"

This works great. Now I’d like my photon to do the call, so I’m trying to set up a web-hook. Inevitably, no matter what I’ve tried I get an error. Here are a couple of things i’ve tried, and their associated errors:

 {"error":{"errors":[{"domain":"geolocation","reason":"notFound","message":"Not Found"}],"code":404,"message":"Not Found"}}

This API does not support parsing form-encoded input.

Or (the worst), I get a successful call that returns a Lat/Long in DC… I think because the particle server is sending google a blank package, and so Google is trying to use the IP address of the particle server to guess the location:

hook-response/geolocation/0, data: {
  "location": {
  "lat": 39.043756699999996,
  "lng": -77.4874416
 },
 "accuracy": 3439.0
 }

I feel like i’m completely stuck here. Any ideas on how to troubleshoot? I’m fairly certain the issue is with how i’m setting up the webhook.

P.s. WHY?! Why can’t you edit a webhook once you create it!?

This is a particularly challenging web hook to create. I’ll have something in an hour or so, assuming my idea works.

Thanks @rickkas7

In case it’s helpful, here’s some code that I’m using to make the string that I’d like to use for the query:

void scanWiFiNets(){
    String data="[";
    WiFiAccessPoint aps[20];
    int found = WiFi.scan(aps, 20);
    for (int i=0; i<found; i++) {
        WiFiAccessPoint& ap = aps[i];
        data=String(data+"{");
        data=String(data+"\"macAddress\": ");
        data=String(data+"\""+formatMacAddress(ap)+"\",");
        data=String(data+"\"signalStrength\": ");
        data=String(data+String(ap.rssi));
        data=String(data+",");
        data=String(data+"\"channel\": ");
        data=String(data+String(ap.channel));
        data=String(data+"}");
        if (i<(found-1))
            data=String(data+",");
    }
    data=String(data+"]");
    Serial.println(data);
    Particle.publish("locationFromWiFi", data, PRIVATE);
}

String formatMacAddress(WiFiAccessPoint ap){
    return String::format("%02X:%02X:%02X:%02X:%02X:%02X", ap.bssid[0], ap.bssid[1], ap.bssid[2], ap.bssid[3], ap.bssid[4], ap.bssid[5]);
} 

This returns a string formatted as:

 [{"macAddress": "00:11:22:33:44:55","signalStrength": -70,"channel": 1},{"macAddress": "00:AA:BB:CC:DD:FF","signalStrength": -75,"channel": 11}]

Here’s the code I made for the EXACT same purpose. Mind you it only allows 4 Wi-Fi networks to be sent, so it isn’t perfect, but it is still accurate to ~10m, depending on where you are.

#define SCANNED 4

WiFiAccessPoint aps[SCANNED];
String msg;
String mac;
unsigned long t = 0;

void setup() {
    Particle.subscribe("hook-response/geolocation", getLoc, MY_DEVICES);
    Serial.begin(9600);
    while (!Serial.available()) Particle.process();
}

void loop() {
    if (millis()-t > 3500) {
        msg = "{ ";
        WiFi.scan(aps, SCANNED);
        Particle.process();
        for (int x = 0; x < SCANNED; x++) {
            mac = "";
            for (int y= 0; y < 6; y++) {
                mac = mac+String(aps[x].bssid[y], HEX);
                if (y != 5) {
                    mac = mac+":";
                }
            }
            mac.toUpperCase();
            msg = msg+"\"mac"+String(x)+"\": \""+mac+"\", \"sig"+String(x)+"\": \""+String(aps[x].rssi)+"\", \"channel"+String(x)+"\": \""+String(aps[x].channel)+"\"";
            if (x != 3) {
                msg = msg+", ";
            }
            Particle.process();
        }
        msg = msg+" }";
        Serial.println(msg);
        Particle.publish("geolocation", msg, 60, PRIVATE);
        t = millis();
    }
}

void getLoc(const char *name, const char *data) {
    Serial.println(data);
}

And the webhook:

{
    "eventName": "geolocation",
    "url": "https://www.googleapis.com/geolocation/v1/geolocate?key=YOUR_KEY",
    "requestType": "POST",
	"json": {
		"wifiAccessPoints": [
		{
			"macAddress": "{{mac0}}",
			"signalStrength": "{{sig0}}",
			"channel": "{{channel0}}"
		},
		{
			"macAddress": "{{mac1}}",
			"signalStrength": "{{sig1}}",
			"channel": "{{channel1}}"
		},
		{
			"macAddress": "{{mac2}}",
			"signalStrength": "{{sig2}}",
			"channel": "{{channel2}}"
		},
		{
			"macAddress": "{{mac3}}",
			"signalStrength": "{{sig3}}",
			"channel": "{{channel3}}"
		}
		]
	},
	"responseTemplate": "{{location.lat}}~{{location.lng}}~{{accuracy}}",
    "mydevices": true
}
1 Like

I think Random Guy’s solution about as good as it gets. I wasn’t able to dynamically size the array, either. A web hook with the type POST, no for Include Default Data and Custom JSON of the following should be pretty close to working with your publish data.

    {
        "considerIp": "false",
        "wifiAccessPoints":[
            {
                "macAddress":"{{wifiAccessPoints.0.macAddress}}",
                "signalStrength":"{{wifiAccessPoints.0.signalStrength}}",
                "channel":"{{wifiAccessPoints.0.channel}}"
            },
            {
                "macAddress":"{{wifiAccessPoints.1.macAddress}}",
                "signalStrength":"{{wifiAccessPoints.1.signalStrength}}",
                "channel":"{{wifiAccessPoints.1.channel}}"
            }
        ]
    }
1 Like

@randomguy1124 Brilliant! I wasn’t aware you could deal with individual variables in that way ({{mac0}}, {{mac1}}, {{mac2}}) etc.

I owe you a six pack!

1 Like