UK Petrol prices

I didn't realise, but ASDA supermarket publish fuel prices daily for every petrol station in the UK, check it out:

https://storelocator.asda.com/fuel_prices_data.json

I mocked up a little map showing the lowest and highest price today (also shows my local store in Park Royal):

Also because - overkill - I'm logging local / highest / lowest / mean / median / mode prices hourly to InfluxDB :face_with_tears_of_joy: That way I can track fuel prices over the course of weeks and years.

Actually, you may already know - but .gov.uk website has a list of all retailers who publish their fuel prices, I haven't checked yet but it's probably in a standard format - let's hope so. I may even be tempted to querying all the APIs at the same time to find the ultimate cheapest and most expensive fuel in the UK for a given day:

4 Likes

Very nice, thanks for sharing.

For bonus points, now calculate the cheapest from a given location, taking into account the cost of getting to/from the station. :smile: :man_mage:

That would depend on car mpg and how much you wished to fuel up.
I think it would be fair to say anything over 10 miles (probably a lot less) would be a false economy.

Here's a flow that polls all api's and will find all stations within 10 miles approx, ordered by fuel type required.

[{"id":"fe12ef283c46ba4e","type":"inject","z":"d1395164b4eec73e","name":"","props":[{"p":"payload"},{"p":"distance","v":"0.1","vt":"num"},{"p":"lat","v":"50.801530","vt":"num"},{"p":"lon","v":"0.316918","vt":"num"},{"p":"fuel","v":"E10","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"[\"https://applegreenstores.com/fuel-prices/data.json\",\"https://fuelprices.asconagroup.co.uk/newfuel.json\",\"https://storelocator.asda.com/fuel_prices_data.json\",\"https://www.bp.com/en_gb/united-kingdom/home/fuelprices/fuel_prices_data.json\",\"https://fuelprices.esso.co.uk/latestdata.json\",\"https://jetlocal.co.uk/fuel_prices_data.json\",\"https://api2.krlmedia.com/integration/live_price/krl\",\"https://www.morrisons.com/fuel-prices/fuel.json\",\"https://moto-way.com/fuel-price/fuel_prices.json\",\"https://fuel.motorfuelgroup.com/fuel_prices_data.json\",\"https://www.rontec-servicestations.co.uk/fuel-prices/data/fuel_prices_data.json\",\"https://api.sainsburys.co.uk/v1/exports/latest/fuel_prices_data.json\",\"https://www.sgnretail.uk/files/data/SGN_daily_fuel_prices.json\",\"https://www.shell.co.uk/fuel-prices-data.html\",\"https://www.tesco.com/fuel_prices/fuel_prices_data.json\"]","payloadType":"json","x":490,"y":6060,"wires":[["ab0d0df2abeee858"]]},{"id":"ab0d0df2abeee858","type":"split","z":"d1395164b4eec73e","name":"","splt":"\\n","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":false,"addname":"","property":"payload","x":630,"y":6060,"wires":[["1e1b7bed0286ad7c"]]},{"id":"1e1b7bed0286ad7c","type":"change","z":"d1395164b4eec73e","name":"","rules":[{"t":"move","p":"payload","pt":"msg","to":"url","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":790,"y":6060,"wires":[["2ee19cfc3466c7ce"]]},{"id":"2ee19cfc3466c7ce","type":"http request","z":"d1395164b4eec73e","name":"","method":"GET","ret":"obj","paytoqs":"ignore","url":"","tls":"","persist":false,"proxy":"","insecureHTTPParser":false,"authType":"","senderr":false,"headers":[{"keyType":"User-Agent","keyValue":"","valueType":"other","valueValue":"Mozilla/5.0 (iPhone; U; CPU iPhone OS 3_1_2 like Mac OS X; en-us) AppleWebKit/528.18 (KHTML, like Gecko) Version/4.0 Mobile/7D11 Safari/528.16"},{"keyType":"Accept","keyValue":"","valueType":"application/json","valueValue":""},{"keyType":"Accept-Language","keyValue":"","valueType":"en-GB, en-US, en;q=0.9","valueValue":""}],"x":530,"y":6120,"wires":[["52581137ac93d4de"]]},{"id":"52581137ac93d4de","type":"switch","z":"d1395164b4eec73e","name":"","property":"statusCode","propertyType":"msg","rules":[{"t":"eq","v":"200","vt":"num"}],"checkall":"true","repair":true,"outputs":1,"x":730,"y":6120,"wires":[["bce35881c2b3f73c"]]},{"id":"bce35881c2b3f73c","type":"join","z":"d1395164b4eec73e","name":"","mode":"reduce","build":"object","property":"payload","propertyType":"msg","key":"topic","joiner":"\\n","joinerType":"str","useparts":false,"accumulate":true,"timeout":"","count":"","reduceRight":false,"reduceExp":"$append($A, $$.payload.stations)","reduceInit":"[]","reduceInitType":"json","reduceFixup":"","x":890,"y":6120,"wires":[["33551654cad32b49"]]},{"id":"33551654cad32b49","type":"change","z":"d1395164b4eec73e","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"$$.payload[\t   $abs(\t       $number($.location.latitude) - $$.lat\t   ) < $$.distance and         \t   $abs(\t       $number($.location.longitude) - $$.lon\t   ) < $$.distance     \t]^($lookup($.prices, $$.fuel))","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":1060,"y":6120,"wires":[["f6cc12f4a74b0d62"]]},{"id":"f6cc12f4a74b0d62","type":"debug","z":"d1395164b4eec73e","name":"debug 2581","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1090,"y":6060,"wires":[]}]
1 Like

Some fun automation ideas with fuel prices:

  • find the cheapest within x mile radius from a given postcode (or maybe easier based on coordinates)
  • for consultants who travel around: Query your Google calendar and notify you of cheap fuel en-route (Google maps api)
  • Alternatively for people who vary their routes a lot: use geolocation (eg owntracks) to ping you when you are out, and you are not too far away from a cheap retailer
  • reverse engineer the pricing strategies of different retailers. Eg by looking at price distribution by area, etc
  • respond to eg Apple Siri Shortcut: when you press a home screen icon, the shortcut sends your coordinates to NodeRED which returns the prices for the nearest 15 retailers

Any other cool ideas?

Well our nearest petrol station is 8 mile away, so close. What distance does the 0.1 represent as a second supplier is 9 miles away and doesn't show up until 0.3?

Good quick flow though, thank you

Latitude is approx 69 miles per degree, longitude varies a lot more so the 69 miles approx at the equator and 0 at the north/south pole
0.1 is approx 7 mile with latitude approx 5 miles for longitude in the UK, give or take a little.

You could add a separate distance for lat and lon to make it more accurate. Or may be calculate the hypotenuse taking the latitude into account when calculating the longitude. The Distance Between Two Latitude and Longitude Coordinates

I upped the distance to 0.5, which returned 363 stations.

Number 1 in the list is, surprisingly, Esso at 139.7/litre for diesel.
And Number 363 is ...
Oh I thought it was Esso again, but in fact the ones at the end of the list don't have a price for diesel at all.
So the Oscar for the most expensive diesel goes to - of course - Hilton Park Services at 172.9/litre

Does this flow compare coordinate values numerically to find the closest? A possible upgrade might be to use the Haversine formula. There's a possibility that a pure numerical comparison will give incorrect results (although the degree to which they are incorrect and the possibility this is true will depend on one's position on the globe). Maybe for simple / geographically close comparisons this method is good enough?

Shame it's not somehow possible to calculate distance between two postcodes without relying on an API like Google Maps... I wonder if there's a NodeRED addon that leverages downloadable roadmaps to do basic route calculation or similar?

This is very cool. Whenever I see a .json datasource with a map, I think of Grafana Geomap + Infinity Datasource. I set one up here and mapped the colors acc. to the E10 price.

1 Like

I think so as this is not launching a rocket to mars.

Whilst that's true, the error could be as much as 1–2 miles in certain places where longitude distances shrink with latitude. Not so much of an issue in rural areas but in cities probably more so...

Yes, but you can limit that to less by adding a distance for lat and lon separately, and adjusting them for your position north or south of the equator. You could also use pythagorasa's theorem and calculate the hypotenuse of the the lat long positions, again adjusting for your position north or south.

My flow is a example of use and shows me all my local stations using 0.1. I do not need it to be over engineered and out by a mile or two i can live with or adjust

I think the joy of it is that there are many different possible solutions. Haversine formula is reasonably basic trig, and the best way to determine distance as the crow flies as it's universal depending on wherever you are on the globe.

That said, it's also reasonably useless compared with google maps, which accounts for actual roads / driving distance :slight_smile:

Then the calculation would need to be done in a function node as JSONata lacks sin and cos and there are npm libraries haversine - npm

Just arrived home here it is with a haversine function

[{"id":"fe12ef283c46ba4e","type":"inject","z":"d1395164b4eec73e","name":"","props":[{"p":"payload"},{"p":"distance","v":"10","vt":"num"},{"p":"home","v":"{\"latitude\":50.80153,\"longitude\":0.316918}","vt":"json"},{"p":"fuel","v":"E10","vt":"str"},{"p":"unit","v":"mile","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"[\"https://applegreenstores.com/fuel-prices/data.json\",\"https://fuelprices.asconagroup.co.uk/newfuel.json\",\"https://storelocator.asda.com/fuel_prices_data.json\",\"https://www.bp.com/en_gb/united-kingdom/home/fuelprices/fuel_prices_data.json\",\"https://fuelprices.esso.co.uk/latestdata.json\",\"https://jetlocal.co.uk/fuel_prices_data.json\",\"https://api2.krlmedia.com/integration/live_price/krl\",\"https://www.morrisons.com/fuel-prices/fuel.json\",\"https://moto-way.com/fuel-price/fuel_prices.json\",\"https://fuel.motorfuelgroup.com/fuel_prices_data.json\",\"https://www.rontec-servicestations.co.uk/fuel-prices/data/fuel_prices_data.json\",\"https://api.sainsburys.co.uk/v1/exports/latest/fuel_prices_data.json\",\"https://www.sgnretail.uk/files/data/SGN_daily_fuel_prices.json\",\"https://www.shell.co.uk/fuel-prices-data.html\",\"https://www.tesco.com/fuel_prices/fuel_prices_data.json\"]","payloadType":"json","x":490,"y":6060,"wires":[["ab0d0df2abeee858"]]},{"id":"ab0d0df2abeee858","type":"split","z":"d1395164b4eec73e","name":"","splt":"\\n","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":false,"addname":"","property":"payload","x":630,"y":6060,"wires":[["1e1b7bed0286ad7c"]]},{"id":"1e1b7bed0286ad7c","type":"change","z":"d1395164b4eec73e","name":"","rules":[{"t":"move","p":"payload","pt":"msg","to":"url","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":790,"y":6060,"wires":[["2ee19cfc3466c7ce"]]},{"id":"2ee19cfc3466c7ce","type":"http request","z":"d1395164b4eec73e","name":"","method":"GET","ret":"obj","paytoqs":"ignore","url":"","tls":"","persist":false,"proxy":"","insecureHTTPParser":false,"authType":"","senderr":false,"headers":[{"keyType":"User-Agent","keyValue":"","valueType":"other","valueValue":"Mozilla/5.0 (iPhone; U; CPU iPhone OS 3_1_2 like Mac OS X; en-us) AppleWebKit/528.18 (KHTML, like Gecko) Version/4.0 Mobile/7D11 Safari/528.16"},{"keyType":"Accept","keyValue":"","valueType":"application/json","valueValue":""},{"keyType":"Accept-Language","keyValue":"","valueType":"en-GB, en-US, en;q=0.9","valueValue":""}],"x":530,"y":6120,"wires":[["52581137ac93d4de"]]},{"id":"52581137ac93d4de","type":"switch","z":"d1395164b4eec73e","name":"","property":"statusCode","propertyType":"msg","rules":[{"t":"eq","v":"200","vt":"num"}],"checkall":"true","repair":true,"outputs":1,"x":730,"y":6120,"wires":[["bce35881c2b3f73c"]]},{"id":"bce35881c2b3f73c","type":"join","z":"d1395164b4eec73e","name":"","mode":"reduce","build":"object","property":"payload","propertyType":"msg","key":"topic","joiner":"\\n","joinerType":"str","useparts":false,"accumulate":true,"timeout":"","count":"","reduceRight":false,"reduceExp":"$append($A, $$.payload.stations)","reduceInit":"[]","reduceInitType":"json","reduceFixup":"","x":890,"y":6120,"wires":[["6d2511afd6049cfa"]]},{"id":"6d2511afd6049cfa","type":"function","z":"d1395164b4eec73e","name":"function 156","func":"const distance = (msg.distance || 1);\nconst fuel = (msg.fuel ?? \"E10\");\nconst unit = (msg.unit ?? \"mile\");\nconst home = msg.home;\n\nmsg.payload = msg.payload.filter(obj => {\n    return haversine(\n        home, \n        obj.location,\n        {unit: unit, threshold: distance}\n    ) &&\n    obj.prices[fuel]\n}).sort((a,b) => \n    a.prices[fuel] - b.prices[fuel]\n);\n\nreturn msg;\n","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[{"var":"haversine","module":"haversine"}],"x":1050,"y":6120,"wires":[["f6cc12f4a74b0d62"]]},{"id":"f6cc12f4a74b0d62","type":"debug","z":"d1395164b4eec73e","name":"debug 2581","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1090,"y":6060,"wires":[]}]

If no fuel of type require then the station is removed also.
The units can be km, mile, meter, nmi default mile, distance has a default of 1, can be a float.

Figured out why the second nearest petrol station not shown, Tesco do not include all Tesco Express operations.

A nice open source alternative routing api to google is OSRM API Documentation

The simple correction for the variation in east-west “distance” as you go further north is to multiply by Cosine of the latitude. Eg your 69 miles per degree

1 Like