Skip to content

Commit

Permalink
2025.2.0 (#34)
Browse files Browse the repository at this point in the history
* bump pyfuelprices

* Add PetrolPrices
Config wizard updates
CheapestFuelSensor entities now only update once per update interval
Services now expose optional source parameter
All entities can now contain the source config entry
Runtime data now contains config entry

* Add documentation to repo

* fix Service find_fuel_station has a field source with no name and is not in the translations file

* Update en translation

* update docs url

* restructure docs

* fix documentation build script

* update coordinator exception handling

* fix FuelStationTracker spelling

* auto release creator
  • Loading branch information
pantherale0 authored Feb 11, 2025
1 parent 3fabd23 commit 56511e7
Show file tree
Hide file tree
Showing 20 changed files with 257 additions and 24 deletions.
20 changes: 20 additions & 0 deletions .github/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
changelog:
exclude:
labels:
- ignore-for-release
categories:
- title: Breaking Changes 🛠
labels:
- breaking-change
- title: New Features 🎉
labels:
- enhancement
- title: Bug Fixes 🛠
labels:
- bug-fix
- title: 👒 Dependencies
labels:
- dependencies
- title: Other Changes
labels:
- "*"
24 changes: 24 additions & 0 deletions .github/workflows/create-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
name: Create release

on:
workflow_dispatch:
push:
tags: "*"

permissions:
contents: write

jobs:
publish:
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v3
- uses: cho0o0/[email protected]
with:
generate_release_notes: true
dry_run: true
# Do not use GITHUB_TOKEN if you want to trigger other workflows
timezone: "utc"
api_token: ${{secrets.GITHUB_TOKEN}}
release_title: "${version}"
29 changes: 29 additions & 0 deletions .github/workflows/documentation.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: documentation
on:
push:
branches:
- main
permissions:
contents: write
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Configure Git Credentials
run: |
git config user.name github-actions[bot]
git config user.email 41898282+github-actions[bot]@users.noreply.github.com
- uses: actions/setup-python@v5
with:
python-version: 3.x
- run: echo "cache_id=$(date --utc '+%V')" >> $GITHUB_ENV
- uses: actions/cache@v4
with:
key: mkdocs-material-${{ env.cache_id }}
path: .cache
restore-keys: |
mkdocs-material-
- name: "Install requirements"
run: python3 -m pip install -r "${{ github.workspace }}/requirements.docs.txt"
- run: mkdocs gh-deploy --force
12 changes: 7 additions & 5 deletions custom_components/fuel_prices/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ class FuelPricesConfig:

coordinator: FuelPricesCoordinator
areas: list[dict]
config: ConfigEntry


type FuelPricesConfigEntry = ConfigEntry[FuelPricesConfig]
Expand Down Expand Up @@ -92,10 +93,11 @@ async def handle_fuel_lookup(call: ServiceCall) -> ServiceResponse:
lat = call.data.get("location", {}).get("latitude", default_lat)
long = call.data.get("location", {}).get("longitude", default_long)
fuel_type = call.data.get("type")
source = call.data.get("source", "")
try:
return {
"fuels": await fuel_prices.find_fuel_from_point(
(lat, long), radius, fuel_type
(lat, long), radius, fuel_type, source
)
}
except ValueError as err:
Expand All @@ -110,9 +112,10 @@ async def handle_fuel_location_lookup(call: ServiceCall) -> ServiceResponse:
radius = radius / 1609
lat = call.data.get("location", {}).get("latitude", default_lat)
long = call.data.get("location", {}).get("longitude", default_long)
source = call.data.get("source", "")
try:
locations = await fuel_prices.find_fuel_locations_from_point(
(lat, long), radius
(lat, long), radius, source
)
except ValueError as err:
raise HomeAssistantError(
Expand Down Expand Up @@ -140,7 +143,8 @@ async def handle_force_update(call: ServiceCall):

hass.services.async_register(DOMAIN, "force_update", handle_force_update)

entry.runtime_data = FuelPricesConfig(coordinator=coordinator, areas=areas)
entry.runtime_data = FuelPricesConfig(
coordinator=coordinator, areas=areas, config=entry)

await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)

Expand Down Expand Up @@ -180,8 +184,6 @@ async def async_migrate_entry(hass: HomeAssistant, config_entry: ConfigEntry):
_LOGGER.warning("Removing jet and morrisons from config entry.")
if "morrisons" in new_data[CONF_SOURCES]:
new_data[CONF_SOURCES].remove("morrisons")
if "jet" in new_data[CONF_SOURCES]:
new_data[CONF_SOURCES].remove("jet")
hass.config_entries.async_update_entry(
config_entry, data=new_data, version=3
)
Expand Down
9 changes: 6 additions & 3 deletions custom_components/fuel_prices/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,8 @@
): selector.SelectSelector(
selector.SelectSelectorConfig(
mode=selector.SelectSelectorMode.DROPDOWN,
options=list(SOURCE_MAP),
options=[k for k, v in SOURCE_MAP.items() if v[1] ==
1 and v[2] == 1],
multiple=True,
)
),
Expand Down Expand Up @@ -288,7 +289,8 @@ async def async_step_finished(self, user_input: dict[str, Any] | None = None):
user_input[CONF_SOURCES] = COUNTRY_MAP.get(
self.hass.config.country)
else:
user_input[CONF_SOURCES] = list(SOURCE_MAP)
user_input[CONF_SOURCES] = [
k for k, v in SOURCE_MAP.items() if v[1] == 1 and v[2] == 1]
user_input[CONF_AREAS] = self.configured_areas
user_input[CONF_SCAN_INTERVAL] = self.interval
user_input[CONF_TIMEOUT] = self.timeout
Expand Down Expand Up @@ -502,7 +504,8 @@ async def async_step_finished(self, user_input: dict[str, Any] | None = None):
user_input[CONF_SOURCES] = COUNTRY_MAP.get(
self.hass.config.country)
else:
user_input[CONF_SOURCES] = list(SOURCE_MAP)
user_input[CONF_SOURCES] = [
k for k, v in SOURCE_MAP.items() if v[1] == 1 and v[2] == 1]
user_input[CONF_AREAS] = self.configured_areas
user_input[CONF_SCAN_INTERVAL] = self.interval
user_input[CONF_TIMEOUT] = self.timeout
Expand Down
15 changes: 11 additions & 4 deletions custom_components/fuel_prices/coordinator.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import async_timeout

from homeassistant.core import HomeAssistant
from pyfuelprices import FuelPrices
from pyfuelprices import FuelPrices, UpdateExceptionGroup
from pyfuelprices.sources import UpdateFailedError
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed

Expand All @@ -32,11 +32,18 @@ async def _async_update_data(self):
async with async_timeout.timeout(240):
return await self.api.update()
except TimeoutError as err:
_LOGGER.exception("Timeout updating fuel price data: %s", err)
_LOGGER.exception(
"Timeout updating fuel price data, will retry later: %s", err)
except TypeError as err:
_LOGGER.exception("Error updating fuel price data: %s", err)
_LOGGER.exception(
"Error updating fuel price data, will retry later: %s", err)
except UpdateFailedError as err:
_LOGGER.exception(
"Error communicating with service (%s).", err.status, exc_info=err)
"Error communicating with a service %s", err.status, exc_info=err)
except UpdateExceptionGroup as err:
for e, v in err.failed_providers.items():
_LOGGER.exception(
"Error communicating with service %s - %s", e, v, exc_info=v
)
except Exception as err:
raise UpdateFailed(f"Error communicating with API {err}") from err
17 changes: 13 additions & 4 deletions custom_components/fuel_prices/entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,25 @@

from homeassistant.helpers.entity import Entity
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from homeassistant.config_entries import ConfigEntry

from .coordinator import FuelPricesCoordinator


class FuelStationEntity(CoordinatorEntity):
class FuelPriceEntity:
"""Top level entity type."""

config: ConfigEntry


class FuelStationEntity(FuelPriceEntity, CoordinatorEntity):
"""Represents a fuel station."""

def __init__(
self, coordinator: FuelPricesCoordinator, fuel_station_id, entity_id, source, area, state_value
self, coordinator: FuelPricesCoordinator, fuel_station_id, entity_id, source, area, state_value, config: ConfigEntry
) -> None:
"""Initialize."""
self.config = config
super().__init__(coordinator)
self.coordinator: FuelPricesCoordinator = coordinator
self._fuel_station_id = fuel_station_id
Expand All @@ -36,13 +44,14 @@ def unique_id(self) -> str | None:
return f"fuelprices_{self._fuel_station_id}_{self._entity_id}"


class CheapestFuelEntity(Entity):
class CheapestFuelEntity(FuelPriceEntity, Entity):
"""Represents a fuel."""

def __init__(
self, coordinator: FuelPricesCoordinator, count: str, area: str, fuel: str, coords: tuple, radius: float):
self, coordinator: FuelPricesCoordinator, count: str, area: str, fuel: str, coords: tuple, radius: float, config: ConfigEntry):
"""Initialize."""
self.coordinator: FuelPricesCoordinator = coordinator
self.config = config
self._count = count
self._area = area
self._coords = coords
Expand Down
2 changes: 1 addition & 1 deletion custom_components/fuel_prices/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"xmltodict",
"brotli",
"these-united-states==1.1.0.21",
"pyfuelprices==2025.1.2"
"pyfuelprices==2025.2.2"
],
"ssdp": [],
"version": "0.0.0",
Expand Down
17 changes: 11 additions & 6 deletions custom_components/fuel_prices/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

from homeassistant.components.sensor import SensorEntity
from homeassistant.components.sensor.const import SensorDeviceClass
from homeassistant.const import CONF_LATITUDE, CONF_LONGITUDE, CONF_RADIUS, CONF_NAME, STATE_UNKNOWN
from homeassistant.const import CONF_LATITUDE, CONF_LONGITUDE, CONF_RADIUS, CONF_NAME, STATE_UNKNOWN, CONF_SCAN_INTERVAL
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from pyfuelprices.const import PROP_FUEL_LOCATION_SOURCE
Expand Down Expand Up @@ -41,13 +41,14 @@ async def async_setup_entry(
):
if station["id"] not in found_entities:
entities.append(
FeulStationTracker(
FuelStationTracker(
coordinator=entry.runtime_data.coordinator,
fuel_station_id=station["id"],
entity_id="devicetracker",
source=station["props"][PROP_FUEL_LOCATION_SOURCE],
area=area[CONF_NAME],
state_value=state_value
state_value=state_value,
config=entry
)
)
found_entities.append(station["id"])
Expand All @@ -62,12 +63,13 @@ async def async_setup_entry(
area=area[CONF_NAME],
fuel=area[CONF_CHEAPEST_SENSORS_FUEL_TYPE],
coords=(area[CONF_LATITUDE], area[CONF_LONGITUDE]),
radius=area[CONF_RADIUS]
radius=area[CONF_RADIUS],
config=entry
))
async_add_entities(entities, True)


class FeulStationTracker(FuelStationEntity, SensorEntity):
class FuelStationTracker(FuelStationEntity, SensorEntity):
"""A fuel station entity."""

@property
Expand Down Expand Up @@ -148,7 +150,10 @@ async def async_update(self) -> None:
)
if len(data) >= (int(self._count)-1):
self._last_update = datetime.now()
self._next_update = datetime.now() + timedelta(minutes=5)
self._next_update = datetime.now() + timedelta(minutes=self.config.options.get(
CONF_SCAN_INTERVAL, self.config.data.get(
CONF_SCAN_INTERVAL, 1440)
))
if len(data) >= self._count:
self._cached_data = data[int(self._count)-1]
else:
Expand Down
10 changes: 10 additions & 0 deletions custom_components/fuel_prices/services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ find_fuel_station:
selector:
location:
radius: true
source:
required: false
selector:
text:
multiline: false
find_fuels:
fields:
location:
Expand All @@ -18,3 +23,8 @@ find_fuels:
selector:
text:
multiline: false
source:
required: false
selector:
text:
multiline: false
8 changes: 8 additions & 0 deletions custom_components/fuel_prices/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,10 @@
"type": {
"name": "Fuel Type",
"description": "The fuel type to search for (such as E5, E10, B7, SDV)"
},
"source": {
"name": "Data Source to search",
"description": "The data source ID to search, defaults to 'any' for all data sources."
}
}
},
Expand All @@ -91,6 +95,10 @@
"location": {
"name": "Location",
"description": "The location of the area to search"
},
"source": {
"name": "Data Source to search",
"description": "The data source ID to search, defaults to 'any' for all data sources."
}
}
}
Expand Down
8 changes: 8 additions & 0 deletions custom_components/fuel_prices/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,10 @@
"type": {
"name": "Fuel Type",
"description": "The fuel type to search for (such as E5, E10, B7, SDV)"
},
"source": {
"name": "Data Source to search",
"description": "The data source ID to search, defaults to 'any' for all data sources."
}
}
},
Expand All @@ -91,6 +95,10 @@
"location": {
"name": "Location",
"description": "The location of the area to search"
},
"source": {
"name": "Data Source to search",
"description": "The data source ID to search, defaults to 'any' for all data sources."
}
}
}
Expand Down
1 change: 1 addition & 0 deletions docs/.pages
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
title: Fuel Prices
15 changes: 15 additions & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Getting Started

This is a data finder integration to retrieve local (or remote) fuel price data for Home Assistant using the `pyfuelprices` library. This library aims to provide the most extensive set of data sources for fuel prices in the world.

You can use this service to:

- Track fuel prices in your local area
- Query for fuel prices in an automation
- Calculate how much it will cost to fill your tank of fuel
- Find the cheapest station near a entity providing latitude and longitude (script required)

## Warnings

- Commercial usage of this integration and its Python library is strictly prohibited.
- You may fork and modify as you require or contribute to the project freely.
Loading

0 comments on commit 56511e7

Please sign in to comment.