"Alexa? Which bins do I need to take out?" - Reverse engineering the Bexley Council bin collection API
A few years ago I wrote an Alexa skill to see what bin day it was. The skill only worked with my local council - Oxford.
I've since moved house and wanted to recreate the service for my new council - Bexley.
Demo
Brilliant, eh?
The API
Bexley Council don't publish an API. I initially thought about scraping their bin collection day web-form but I couldn't work out the calls the site made.
But Bexley have an app! Well, they use the @HOME app along with a bunch of councils. The app is... not very good. And it isn't particularly secure. Which is good news for me, as I was able to intercept all the traffic between it and its server!
The API Call
You need to register an email address along with your home address. You pass that as your username. If you know a resident's email address, you can probably find out their bin collection day. I don't know if that's a GDPR thing or not...
curl --header "X-email: whatever@example.com" "https://services.athomeapp.net/ServiceData/GetUserRoundJson"
The app also passes --header "X-country: gb" --header "X-udprn: 1234567"
- that last one is your UDPRN a sort of standard reference for addresses. You don't need to supply them though.
The Response
You get back JSON about when your bins are going to be collected. I've not yet seen how it handles bank holidays. I've redacted any person information.
{
"email": "whatever@example.com",
"udprn": "",
"managed": true,
"userrounds": [{
"roundid": "1234",
"collectionday": "Fri",
"containerid": "abcd",
"collectioninterval": "Every 2 week",
"containerdescription": "Chargeable garden waste bin",
"containername": "Chargeable garden waste bin",
"nextcollectiondates": [{
"day": "18",
"month": "10",
"year": "2019",
"hours": "0",
"mins": "0",
"datestring": "18 10 2019 12:00",
"isaltered": false,
"alteredmessage": null,
"weeknumber": 0
}, {
"day": "1",
"month": "11",
"year": "2019",
"hours": "0",
"mins": "0",
"datestring": "01 11 2019 12:00",
"isaltered": false,
"alteredmessage": null,
"weeknumber": 0
}, {
"day": "15",
"month": "11",
"year": "2019",
"hours": "0",
"mins": "0",
"datestring": "15 11 2019 12:00",
"isaltered": false,
"alteredmessage": null,
"weeknumber": 0
}, {
"day": "29",
"month": "11",
"year": "2019",
"hours": "0",
"mins": "0",
"datestring": "29 11 2019 12:00",
"isaltered": false,
"alteredmessage": null,
"weeknumber": 0
}, {
"day": "13",
"month": "12",
"year": "2019",
"hours": "0",
"mins": "0",
"datestring": "13 12 2019 12:00",
"isaltered": false,
"alteredmessage": null,
"weeknumber": 0
}, {
"day": "27",
"month": "12",
"year": "2019",
"hours": "0",
"mins": "0",
"datestring": "27 12 2019 12:00",
"isaltered": false,
"alteredmessage": null,
"weeknumber": 0
}],
"containeritems": null,
"roundbitmask": 0,
"selected": true
}, {
"roundid": "2345",
"collectionday": "Fri",
"containerid": "xyz",
"collectioninterval": "Every week",
"containerdescription": "Food recycling box",
"containername": "Food recycling box",
"nextcollectiondates": [{
"day": "18",
"month": "10",
"year": "2019",
"hours": "0",
"mins": "0",
"datestring": "18 10 2019 12:00",
"isaltered": false,
"alteredmessage": null,
"weeknumber": 0
}],
"containeritems": null,
"roundbitmask": 0,
"selected": true
}, {
"roundid": "987654",
"collectionday": "Fri",
"containerid": "qwerty",
"collectioninterval": "Every week",
"containerdescription": "Glass box",
"containername": "Glass box",
"nextcollectiondates": [{
"day": "18",
"month": "10",
"year": "2019",
"hours": "0",
"mins": "0",
"datestring": "18 10 2019 12:00",
"isaltered": false,
"alteredmessage": null,
"weeknumber": 0
}],
"containeritems": null,
"roundbitmask": 0,
"selected": true
}, {
"roundid": "asdf",
"collectionday": "Fri",
"containerid": "zxcvb",
"collectioninterval": "Every week",
"containerdescription": "Paper & Cardboard",
"containername": "Paper & Cardboard",
"nextcollectiondates": [{
"day": "18",
"month": "10",
"year": "2019",
"hours": "0",
"mins": "0",
"datestring": "18 10 2019 12:00",
"isaltered": false,
"alteredmessage": null,
"weeknumber": 0
}],
"containeritems": null,
"roundbitmask": 0,
"selected": true
}, {
"roundid": "poiuyt",
"collectionday": "Fri",
"containerid": "lkjhg",
"collectioninterval": "Every week",
"containerdescription": "Plastic and Cans",
"containername": "Plastic and Cans",
"nextcollectiondates": [{
"day": "18",
"month": "10",
"year": "2019",
"hours": "0",
"mins": "0",
"datestring": "18 10 2019 12:00",
"isaltered": false,
"alteredmessage": null,
"weeknumber": 0
}],
"containeritems": null,
"roundbitmask": 0,
"selected": true
}, {
"roundid": "qazwsx",
"collectionday": "Fri",
"containerid": "wsxedc",
"collectioninterval": "Every 2 week",
"containerdescription": "Waste wheeled bin",
"containername": "Waste wheeled bin",
"nextcollectiondates": [{
"day": "25",
"month": "10",
"year": "2019",
"hours": "0",
"mins": "0",
"datestring": "25 10 2019 12:00",
"isaltered": false,
"alteredmessage": null,
"weeknumber": 0
}, {
"day": "8",
"month": "11",
"year": "2019",
"hours": "0",
"mins": "0",
"datestring": "08 11 2019 12:00",
"isaltered": false,
"alteredmessage": null,
"weeknumber": 0
}, {
"day": "22",
"month": "11",
"year": "2019",
"hours": "0",
"mins": "0",
"datestring": "22 11 2019 12:00",
"isaltered": false,
"alteredmessage": null,
"weeknumber": 0
}, {
"day": "6",
"month": "12",
"year": "2019",
"hours": "0",
"mins": "0",
"datestring": "06 12 2019 12:00",
"isaltered": false,
"alteredmessage": null,
"weeknumber": 0
}, {
"day": "20",
"month": "12",
"year": "2019",
"hours": "0",
"mins": "0",
"datestring": "20 12 2019 12:00",
"isaltered": false,
"alteredmessage": null,
"weeknumber": 0
}],
"containeritems": null,
"roundbitmask": 0,
"selected": true
}]
}
Yeah, not the greatest scrap of JSON I've seen. And a datestring of 18 10 2019 12:00
is particularly painful.
But, you know what? It gets the job done. I was able to parse through it to get what bin day it is.
Building the skill
I've written a quick guide to self-hosting an Alexa skill.
I can't release this to the public because it uses personal information. And I don't want to be responsible for storing your email address or physical address.
The crappy source code is on GitLab. Enjoy!
Richard Kavanagh says:
You say you need to register an email address. Where do you register it?
@edent says:
In the @HOME app.
James O'Malley said on twitter.com:
A brilliant public service for some enterprising nerd would be to reverse engineer every local authority’s bin day API and document how to make calls to it. Like @edent has done with Bexley here:
shkspr.mobi/blog/2019/11/a…