Queensland Fuel Prices Integration

Hey all

Would anyone be interested in helping me get data from the FuelPricesQld API into an integration? I don’t know how to start the process.

I have access to the API (free for data consumers), and have retrieved the data I would like to build into HA via Postman (daily prices for my fuel type for all local service stations). The data comes back in json format… Looking at this I think it may be fairly straight forward for someone who knows what they are doing…

The following request provides the data I want
https://fppdirectapi-prod.fuelpricesqld.com.au/Price/GetSitesPrices?countryId=21&geoRegionLevel=2&geoRegionId=16

This would need to be combined with this request to get friendly names for the site ids
https://fppdirectapi-prod.fuelpricesqld.com.au/Subscriber/GetFullSiteDetails?countryId=21&geoRegionLevel=2&geoRegionId=16

I imagine the end result could be set up via the gui, users add in their API after applying for one, and then set their GeoRegionLevel / GeoRegionID / FuelID (could add multiple) etc and it can create entities for each station / price.

Cheers

Hi, found this old post. I have actually written and integration for Qld but the datafeed/API I was using has been shutdown. I’d be happy to work with you on using the feed you spoke about, I tried your links but they failed to return a response. -Tony.

Hey

Those links don’t work outside of using Postman or something to interrogate the API with authorisation.

To get access to the API start here - https://www.fuelpricesqld.com.au/. Can fill in the following form and you’ll get an email with API access and instructions to get the data in Postman. I can’t get the swagger stuff to do anything.

anyone progress further here? I saw someone in the Australian FB group get it going but didnt’ share the code only screenshots… I have my API from https://www.fuelpricesqld.com.au/ but not sure how to progress… very green on all this stuff.

hhey @tbgoose @tonymyatt I created this sensor and it seems to work… I need to watch it over the next few days if it change… one monitored servo did so it’s looking positive…

you need to use postman to work out what servo’s you want to monitor and what fuel type.

1 Like

yes it works and I’ve run it for a few days now
image

2 Likes

So this is my implementation, which is a little different, but I think it gets to the same end. I couldn’t use the geoRegionID, as I have multiple service stations that I am interested in with the same geoRegionID.

First I access the fuel set of fuel prices through the API, the following is in my configuration.yaml.

rest:
  - scan_interval: 3001
    resource: https://fppdirectapi-prod.fuelpricesqld.com.au/Price/GetSitesPrices?countryId=21&geoRegionLevel=3&geoRegionId=1
    headers:
      Authorization: FPDAPI SubscriberToken= *token stuff goes here*
      Content-Type: application/json
    sensor:
      - name: fuel_prices_all
        value_template: "prices"
        json_attributes:
          - SitePrices

Then I created a number of InputNumber Helpers, via “Settings > Devices & Services > Helpers”
Add a helper and set them up like this:

I had to manually go back into each input and set the Area, as this wasn’t part of the initial setup of the helper.

Then I set up an Automation, like this:

alias: Fuel_Price_Automation
description: >-
  Define and automate the extraction of the price of e10 at the Taigum Liberty
  Servo
trigger:
  - platform: state
    entity_id:
      - sensor.fuel_prices_all
condition: []
action:
  - service: input_number.set_value
    metadata: {}
    data:
      value: |-
        {% set data = 
          state_attr('sensor.fuel_prices_all', 'SitePrices') %}
          {% set filtered_prices = data | selectattr('SiteId', 'eq', 61402386) | selectattr('FuelId', 'in', [12]) %}
            {% set price = filtered_prices | first %}
            {{ price.Price /1000}}
    target:
      entity_id: input_number.fuel_price_liberty_taigum_e10
  - service: input_number.set_value
    metadata: {}
    data:
      value: |-
        {% set data = 
          state_attr('sensor.fuel_prices_all', 'SitePrices') %}
          {% set filtered_prices = data | selectattr('SiteId', 'eq', 61402386) | selectattr('FuelId', 'in', [3,14]) %}
            {% set price = filtered_prices | first %}
            {{ price.Price /1000}}
    target:
      entity_id: input_number.fuel_price_liberty_taigum_diesel
mode: single

All of which gives me the following:

My next step is to figure out how to get the lowest e10 price shown/highlighted green, and the second lowest as yellow.
But simply looking at them is working for the moment.
Hope this helps

2 Likes

A further alternative method, using a script to dynamically update, filtering can probably be done at the rest level using value_template to limit the number / type of fuel. Triggering can be done from an automation.

If the helper is missing, add / enter device for notification.

alias: Fuel Prices
sequence:
  - variables:
      site_data: '{{ state_attr(''sensor.fuel_sites', ''S'') }}'
      price_data: '{{ state_attr(''sensor.fuel_prices'', ''SitePrices'') }}'
      fuel_types: '{{ state_attr(''sensor.fuel_types'', ''Fuels'') }}'
  - repeat:
      count: '{{ site_data | length }}'
      sequence:
        - variables:
            site: '{{ site_data[repeat.index - 1] }}'
            site_id: '{{ site.S }}'
            site_name: '{{ site.N }}'
        - repeat:
            count: >-
              {{ price_data | selectattr('SiteId', 'eq', site_id) | list |
              length }}
            sequence:
              - variables:
                  matched_prices: >-
                    {{ price_data | selectattr('SiteId', 'eq', site_id) | list
                    }}
                  fuel_price: '{{ matched_prices[repeat.index - 1] }}'
                  fuel_id: '{{ fuel_price.FuelId }}'
                  fuel_name: >
                    {{ fuel_types | selectattr('FuelId', 'eq', fuel_id) |
                    map(attribute='Name') | first | slugify }}
                  dynamic_var_name: '{{ ''fuel_price_'' ~ slugify(site_name) ~ ''_'' ~ fuel_name }}'
                  dynamic_var_entity_id: input_number.{{ dynamic_var_name }}
              - choose:
                  - conditions: '{{ not states(dynamic_var_entity_id) }}'
                    sequence:
                      - data:
                          title: Missing Input Number Helper
                          message: >
                            "The input_number helper '{{ dynamic_var_entity_id }}'
                            for Site '{{ site_name }}' and Fuel '{{ fuel_name
                            }}' (Fuel ID: {{ fuel_id }}) is missing."
                        action: notify.test_mobile_phone
              - target:
                  entity_id: '{{ dynamic_var_entity_id }}'
                data:
                  value: '{{ fuel_price.Price }}'
                action: input_number.set_value
description: ''
icon: mdi:fuel

As an extension to this I’m using the below in a markdown card to auto scrape all and provide the lowest price - has to all be on one line for in-card formatting… unless I’m missing something. If anyone knows how to right align in the markdown card, drop a reply ! :

type: markdown
content: >-
  # Cheapest Fuel Prices
  ---
  {% set fuel_types = state_attr('sensor.fuel_types', 'Fuels') %}{% for fuel in
  fuel_types %}{% set fueltype = fuel.Name | lower | slugify%}{% set
  capitalized_fueltype = fueltype.title() |replace('_',' ') %}{% set
  fuel_entities = states.input_number | selectattr('entity_id', 'search',
  fueltype~'$') | list %}{% if fuel_entities | length > 0 %} {{
  capitalized_fueltype }} : {% set values = fuel_entities |
  map(attribute='state') | list %}{% set min_value = values | min %}{% set
  lowest_entities = fuel_entities | selectattr('state', 'equalto', min_value) |
  list %}{% if lowest_entities | length > 0 %}{% for entity in lowest_entities
  %}{% set name_without_fueltype = entity.name.replace(fueltype,
  '').replace(capitalized_fueltype, '') %} {{ name_without_fueltype }} - ${{
  entity.state | float }}{% if not loop.last %}, {% endif %}{% endfor %}{% endif
  %}

  {% else %}{% endif %}{% endfor %}

Welcome any feedback. Hopefully not bad for a 1st post :slight_smile:

2 Likes

my head’s spinning a bit on this one - from what I’m reading, I really need the Postman app to work out site ID’s, etc to then use some of the magic you guys have come up with to drill down to servo’s closer to home?

@Bhrudwo - hey Chris,

have a look at the auto-entities card for sorting your entries. GitHub - thomasloven/lovelace-auto-entities: 🔹Automatically populate the entities-list of lovelace cards

I’m working through your method at the moment (because I kinda understand how you’re doing it)

I take it you have to create a Helper and Automation per site?

Hey, yes Postman is a must… I tried pulling all the data first, but there is just too much to handle.

I did a spin through the GetCountryGeographicRegions in Postman, pulled the lot and looked for places round me - then from there used that GeoRegionID, and the georegionlevel in the same JSON against the siteprices and fullsitedetails.

Excerpt from my configuration.yaml below :

rest:
  - resource: https://fppdirectapi-prod.fuelpricesqld.com.au/Price/GetSitesPrices?countryId=21&geoRegionLevel=2&geoRegionId=xxxxxx
    headers:
      Authorization: !secret FuelPriceAPI
      Content-Type: application/json
    sensor:
      - name: fuel_prices
        value_template: "prices"
        json_attributes:
          - SitePrices
    scan_interval: 300
  - resource: https://fppdirectapi-prod.fuelpricesqld.com.au/Subscriber/GetFullSiteDetails?countryId=21&geoRegionLevel=2&geoRegionId=xxxxxx
    headers:
      Authorization: !secret FuelPriceAPI
      Content-Type: application/json
    sensor:
      - name: fuel_sites
        value_template: "sites"
        json_attributes:
          - S
    scan_interval: 86400
  - resource: https://fppdirectapi-prod.fuelpricesqld.com.au/Subscriber/GetCountryFuelTypes?countryId=21
    headers:
      Authorization: !secret FuelPriceAPI
      Content-Type: application/json
    sensor:
      - name: fuel_types
        value_template: "fuels"
        json_attributes:
          - Fuels
    scan_interval: 86400
1 Like

thanks very much - we’ve just bought in Thornlands - moving up from Melbourne, so it’s a novel concept to be moving to a state that has fuel price reporting!!

Thanks mate - so, I have it pulling back data from the API, but can’t for the life of me get your script to produce any data…

have you got any other config that glues it all together?

Seen the light and moving to the Sun :smiley: !

OK, so if you have the rest sensors pulling in data, the API side is good.

The main chunk of code (the one starting with alias: Fuel Prices) is for a Script, confirm that one is setup. I’ve got an automation that triggers every time the API poll happens and the data changes (so relies on the scan_interval: 300 in the REST sensor), and then starts the script. Set it up but disable it for now… Automation YAML is below

alias: Fuel Prices
description: Update fuel prices when REST is refreshed
mode: single
triggers:
  - entity_id:
      - sensor.fuel_prices
    trigger: state
conditions: []
actions:
  - action: script.fuel_prices
    metadata: {}
    data: {}

There is a section in the script

- choose:
                  - conditions:
                      - condition: template
                        value_template: "{{ not states(dynamic_var_entity_id) }}"
                    sequence:
                      - data:
                          title: Missing Input Number Helper
                          message: >
                            "The input_number helper '{{ dynamic_var_entity_id
                            }}' for Site '{{ site_name }}' and Fuel '{{
                            fuel_name }}' (Fuel ID: {{ fuel_id }}) is missing."
                        action: notify.test_mobile_phone

That can, if you set the action to be your mobile, send notifications via the companion app and alert you to missing helpers. In case you miss any of the helpers.

The last bit is the helpers, I’ve got helpers for each fuel type at each servo that the API returns, like :
input_number.fuel_price_ampol_foodary_annerley_e10 - setup like the screen grab from Bhrudwo above.

Once all those are setup, run the automation manually and see if any data hits the helpers

yeah, definitely seen the light :smiley:

ok, so I need a helper for every fuel type/servo that I want to track - I have a couple returnng values, but for some reason the markdown card isn’t playing ball…

will go tear my hair out over that one now… lol

Yeah, I ended up with ~60 or so helpers… I may have gone a little overboard :smiley:

All my helper entity_id’s are lower case, and that does definitely count, as the match from the lower case fuel type won’t match otherwise - not sure if that’s your issue.

I will say the markdown card took me a few days to figure out…

I wanted to contribute a guide with my own implementation of this after many hours of trial and error.

Why this method?

  • Combines fuel types into a single sensor for a fuel station.
  • Easily add more stations or fuel types in your configuration
  • Once set up add new station in only 2 lines (Just entity name and state needed to add another sensor)
  • No manual helpers needed to access large amounts of data
  • Clean configs
  • Add another station in seconds
  • Configures icon to gas station from config
  • Configures unit of measurement to ‘¢/L’ or ‘$/L’

Downloading the data

As always you need a “Data consumer” API KEY from fuelpricesqld.com.au which you will put in your secrets.yaml as FUEL_API_TOKEN: FPDAPI SubscriberToken=XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX

I am using @thechancer 's code for getting the JSON data into home assistant.

configuration.yaml:

rest:
  - resource: https://fppdirectapi-prod.fuelpricesqld.com.au/Subscriber/GetCountryFuelTypes?countryId=21
    headers:
      Authorization: !secret FUEL_API_TOKEN
      Content-Type: application/json
    sensor:
      - name: fuel_types
        value_template: "fuels"
        json_attributes:
          - Fuels
    scan_interval: 86400
  - resource: https://fppdirectapi-prod.fuelpricesqld.com.au/Subscriber/GetFullSiteDetails?countryId=21&geoRegionLevel=1&geoRegionId=XYZ
    headers:
      Authorization: !secret FUEL_API_TOKEN
      Content-Type: application/json
    sensor:
      - name: fuel_sites
        value_template: "sites"
        json_attributes:
          - S
    scan_interval: 86400
  - resource: https://fppdirectapi-prod.fuelpricesqld.com.au/Price/GetSitesPrices?countryId=21&geoRegionLevel=1&geoRegionId=XYZ
    headers:
      Authorization: !secret FUEL_API_TOKEN
      Content-Type: application/json
    sensor:
      - name: fuel_prices
        value_template: "prices"
        json_attributes:
          - SitePrices
    scan_interval: 300

NOTE: If copying this data, change the geoRegionId from XYZ to your suburb, or change geoRegionLevel from 1 (suburb level) to 2 (city level) or 3 (state level) data collection

Creating the JINJA template to avoid re-using code:
A JINJA template allows you to re-use redundant (identical) code, this reduces the code from 5-10 lines to 1 line in EACH ATTRIBUTE that only shows the data that needs to be edited, I cannot overstate enough how much cleaner and less redundant this made my config.

This is mine located at /config/custom_templates/fuel.jinja:

{% macro fuel_price(siteID, fuelID) %}
{{ state_attr('sensor.fuel_prices', 'SitePrices') | selectattr('SiteId', '==', siteID) | selectattr('FuelId', '==', fuelID) | map(attribute='Price') | first | float / 10}} 
{% endmacro %}

The macro receives an input of siteID and fuelID (values from the API, use POSTman to get these values easily), selects the correct data, takes the 4 digit price value (e.g 1234) and divides by 10 to give you cents per litre (123.4¢/L), you can alternatively change the division to 1000 to give you dollars per litre ($1.234/L).

(RECOMMENDED) Create a sensor for a combined fuel station with multiple fuel types as attributes:
I created template sensors neatly using the previous JINJA template in sensors.yaml (or configuration.yaml if you have not separated your sensors into a separate file).

Change all instances of siteId to the same fuel station, change the fuelId in the state line to the fuel type you primarily fill with and change the FuelTypeX and fuelId to fuels you are interested in from that station.

template:
  - sensor:
      - name: "Fuel Station Name"
        icon: mdi:gas-station
        state: "{% from 'fuel.jinja' import fuel_price %}{{ fuel_price(siteId, fuelId) }}"  
        unit_of_measurement: '¢/L'
        attributes:
          FuelType1: "{% from 'fuel.jinja' import fuel_price %}{{ fuel_price(siteId, fuelId }}"
          FuelType2: "{% from 'fuel.jinja' import fuel_price %}{{ fuel_price(siteId, fuelId) }}" 

EXAMPLE
sensors.yaml, where siteId is 61401118 (7/11 Vulture Street), the primary fuelIDs is 12 (e10), and also includes 5 (Unleaded 95), 8 (Unleaded 98), and 3 (Diesel) as attributes:

template:
  - sensor:
      - name: "7eleven East Brisbane (Vulture Street)"
        icon: mdi:gas-station
        state: "{% from 'fuel.jinja' import fuel_price %}{{ fuel_price(61401118, 12) }}"  
        unit_of_measurement: '¢/L'
        attributes:
          E10: "{% from 'fuel.jinja' import fuel_price %}{{ fuel_price(61401118, 12) }}"
          U95: "{% from 'fuel.jinja' import fuel_price %}{{ fuel_price(61401118, 5) }}" 
          U98: "{% from 'fuel.jinja' import fuel_price %}{{ fuel_price(61401118, 8) }}"
          Diesel: "{% from 'fuel.jinja' import fuel_price %}{{ fuel_price(61401118, 3) }}"

NOTE: The siteID MUST be within the data you downloaded (for me this was GeoRegionId as my suburb). The sensor will fail if the site is not downloaded

Create a sensor for a fuel station’s individual fuel type:

This option is if you:

  • Only care about a single fuel type (E.g if you only fill up with E10)
  • Want to use a card that does not allow you to select attributes, only states
  • If you want to create Template sensors in the GUI instead of yaml
  • Prefer one sensor for each fuel station’s fuel type

NOTE: This can get messy if you track lots of stations or fuels using this method. An example helpers list could include: Fuel Station 1 E10, Fuel Station 1 U95, Fuel Station 1 E98, Fuel Station 1 Diesel, Fuel Station 2 E10, ... etc.). You are essentially creating a helper for a single station's single fuel type

{% from 'fuel.jinja' import fuel_price %}{{ fuel_price(61401118, 12) }}

Summary
In my sensors.yaml I am using 26 lines to track 4 fuel types from 3 stations. That show up as helpers for each station with the primary fuel type as the state and 3 alternate fuel types in the attributes which can be exposed using the Tile Card.

To track another station, copy the sensor definition from - name: and place it below the one above it, then change the siteID in each of the new lines. Very quick and easy to add new stations.

Let me know if there are any issues or errors, but this is working perfectly for me.