How to Add Real UK Fuel (Petrol Diesel) Prices to Home Assistant

I wanted to create a tracker for a local petrol station prices on my HA dashboard one which showed when the prices were high and low over time.

petrolprices.com has some really good data but no API.
for example https://www.petrolprices.com/locations/london/caledonian-road/593?m=1

So I ended up doing the following:

Step 1 – Install the Python dependencies (once only)

Open Terminal & SSH add-on and run:

Bash

pip3 install requests beautifulsoup4 --break-system-packages

Step 2 – Create the scraper script

File → File editor → create folder /config/scripts/ → new file fetch_fuel_prices.py

Python

#!/usr/bin/env python3

import requests
from bs4 import BeautifulSoup
import re

url = 'https://www.petrolprices.com/locations/st-davids/haverfordwest-road/8443'
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'}

response = requests.get(url, headers=headers)
soup = BeautifulSoup(response.text, 'html.parser')
text = soup.get_text()

# Extract prices using regex (matches page structure from logs)
unleaded = re.search(r'UNLEADED\s*([0-9]+\.[0-9]+)p', text)
super_unleaded = re.search(r'SUPER UNLEADED\s*([0-9]+\.[0-9]+)p', text)
diesel = re.search(r'DIESEL\s*([0-9]+\.[0-9]+)p', text)
premium_diesel = re.search(r'PREMIUM DIESEL\s*([0-9]+\.[0-9]+)p', text)

# Output as pipe-separated string (safe, short)
prices = [
    unleaded.group(1) if unleaded else 'N/A',
    super_unleaded.group(1) if super_unleaded else 'N/A',
    diesel.group(1) if diesel else 'N/A',
    premium_diesel.group(1) if premium_diesel else 'N/A'
]
print('|'.join(prices))

Save → Terminal → test it:

Bash

chmod +x /config/scripts/fetch_fuel_prices.py
python3 /config/scripts/fetch_fuel_prices.py
# → should return: 134.9|152.9|144.9|164.9

Step 3 – Add to configuration.yaml (exact copy-paste)

YAML

command_line:
  - sensor:
      name: "St Davids Fuel Prices Raw"
      unique_id: fuel_prices_raw_st_davids
      command: "python3 /config/scripts/fetch_fuel_prices.py"
      scan_interval: 7200   # 2 hours (86400 = once per day)
      value_template: "{{ value }}"

template:
  - sensor:
      - name: "St Davids Unleaded"
        unique_id: fuel_st_davids_unleaded
        unit_of_measurement: "ppl"
        device_class: monetary
        state_class: measurement
        icon: mdi:gas-station
        state: >
          {% set p = states('sensor.st_davids_fuel_prices_raw').split('|') %}
          {{ p[0] | trim | float(default=0) if p|length >= 4 and p[0] != 'N/A' else none }}

      - name: "St Davids Super Unleaded"
        unique_id: fuel_st_davids_super_unleaded
        unit_of_measurement: "ppl"
        device_class: monetary
        state_class: measurement
        icon: mdi:gas-station
        state: >
          {% set p = states('sensor.st_davids_fuel_prices_raw').split('|') %}
          {{ p[1] | trim | float(default=0) if p|length >= 4 and p[1] != 'N/A' else none }}

      - name: "St Davids Diesel"
        unique_id: fuel_st_davids_diesel
        unit_of_measurement: "ppl"
        device_class: monetary
        state_class: measurement
        icon: mdi:gas-station
        state: >
          {% set p = states('sensor.st_davids_fuel_prices_raw').split('|') %}
          {{ p[2] | trim | float(default=0) if p|length >= 4 and p[2] != 'N/A' else none }}

      - name: "St Davids Premium Diesel"
        unique_id: fuel_st_davids_premium_diesel
        unit_of_measurement: "ppl"
        device_class: monetary
        state_class: measurement
        icon: mdi:gas-station
        state: >
          {% set p = states('sensor.st_davids_fuel_prices_raw').split('|') %}
          {{ p[3] | trim | float(default=0) if p|length >= 4 and p[3] != 'N/A' else none }}

Step 4 – Test then Reload or restart

Check configuration → Restart Home Assistant (or just reload command_line and template integrations).

Wait 30 seconds → Developer Tools → States → you will see four perfect price sensors.

Want another station?

Just copy the Python script, change the URL, and duplicate the YAML blocks with new names/IDs.

Hope this helps.

Beautiful Soup can be a powerful scraper/parser, but watch out when the site you are scraping updates their web site pages and you have to go back to basics.

Is there a PetrolPrices add-on? Maybe you should package up your with a GUI to select different outlets and offer it up?

Check with PetrolPrices - they may be developing an API and need testers or already have it but it is hidden for those in the know.
With a name like “Automate App Limited” I suspect that it may be the latter, that they use in conjunction with their phone app.

100% I’m sure this will break as soon as they notice people using it. So I’ve set mine to only grab data 4 times a day.

If you look for the JSON that builds the site, developers usually don’t change that. In chrome, F12. Watch the page load. Find the request that grabs the pre-flight JSON. Right click, copy as curl bash. Go to curlconverter.com to see the header structure. Mimic it in reqeusts, make your request and you’re done. If you want to get fancy, use proxies, random User Agent generation, or any number of other tricks to make it look like you’re a human. Oh and use sleep timers. Why no one does is beyond me. Its the easiest way to get caught when you’re just ringing the servers front doorbell with reckless abandonment.