Skip to content

Commit

Permalink
2024.1.0 (#3)
Browse files Browse the repository at this point in the history
  • Loading branch information
pantherale0 authored Jan 14, 2024
1 parent 5896059 commit b53ed84
Show file tree
Hide file tree
Showing 10 changed files with 515 additions and 83 deletions.
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,14 @@ _Integration to integrate with [pyfuelprices][pyfuelprices]._
1. Restart Home Assistant
1. In the HA UI go to "Configuration" -> "Integrations" click "+" and search for "Fuel Prices"

## Privacy notice

This integration relies entirely on cloud services, alongside this a few libraries are used to geocode provided coordinates into location data for certain providers such as GasBuddy or TankerKoenig.

For reverse geocoding a mix of Nominatim (https://nominatim.org/), these-united-states (https://pypi.org/project/these-united-states/) and reverse-geocode (https://pypi.org/project/reverse-geocode/). This is done to improve performance, for example, looking up provided coordinates with reverse-geocode will allow us to restrict the fuel station search to data providers available in only that country.

Similar to this, this integration will use these-united-states to retrieve the state of given coordinates, and finally Nominatim is used to retrieve the nearest postcode for the TankerKoenig data source.

## Configuration is done in the UI

<!---->
Expand Down
100 changes: 68 additions & 32 deletions custom_components/fuel_prices/__init__.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,22 @@
"""Fuel Prices integration."""

import contextlib
import logging

from datetime import timedelta

from pyfuelprices import FuelPrices
from pyfuelprices.const import PROP_AREA_LAT, PROP_AREA_LONG, PROP_AREA_RADIUS

from homeassistant.config_entries import ConfigEntry
from homeassistant.const import Platform
from homeassistant.const import (
Platform,
CONF_LATITUDE,
CONF_LONGITUDE,
CONF_RADIUS,
CONF_TIMEOUT,
CONF_SCAN_INTERVAL,
)
from homeassistant.core import (
HomeAssistant,
ServiceCall,
Expand All @@ -14,23 +25,47 @@
)
from homeassistant.exceptions import HomeAssistantError

from .const import DOMAIN
from .const import DOMAIN, CONF_AREAS, CONF_SOURCES
from .coordinator import FuelPricesCoordinator

_LOGGER = logging.getLogger(__name__)
PLATFORMS = [Platform.DEVICE_TRACKER]


def _build_configured_areas(hass_areas: dict) -> list[dict]:
module_areas = []
for area in hass_areas:
module_areas.append(
{
PROP_AREA_RADIUS: area[CONF_RADIUS],
PROP_AREA_LAT: area[CONF_LATITUDE],
PROP_AREA_LONG: area[CONF_LONGITUDE],
}
)
return module_areas


async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Create ConfigEntry."""
hass.data.setdefault(DOMAIN, {})
_LOGGER.debug("Got request to setup entry.")
sources = entry.options.get(CONF_SOURCES, entry.data.get(CONF_SOURCES, None))
areas = entry.options.get(CONF_AREAS, entry.data.get(CONF_AREAS, None))
timeout = entry.options.get(CONF_TIMEOUT, entry.data.get(CONF_TIMEOUT, 30))
update_interval = entry.options.get(
CONF_SCAN_INTERVAL, entry.data.get(CONF_SCAN_INTERVAL, 1440)
)
default_lat = hass.config.latitude
default_long = hass.config.longitude
try:
fuel_prices: FuelPrices = FuelPrices.create(
enabled_sources=entry.data.get("sources", None)
enabled_sources=sources,
configured_areas=_build_configured_areas(areas),
timeout=timedelta(seconds=timeout),
update_interval=timedelta(minutes=update_interval),
)
await fuel_prices.update()
hass.data[DOMAIN][entry.entry_id] = FuelPricesCoordinator(
with contextlib.suppress(TimeoutError):
await fuel_prices.update()
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = FuelPricesCoordinator(
hass, fuel_prices, entry.entry_id
)
except Exception as err:
Expand All @@ -45,44 +80,44 @@ async def update_listener(hass: HomeAssistant, entry: ConfigEntry):

await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)

def handle_fuel_lookup(call: ServiceCall) -> ServiceResponse:
async def handle_fuel_lookup(call: ServiceCall) -> ServiceResponse:
"""Handle a fuel lookup call."""
radius = call.data.get("location", {}).get(
"radius", 8046.72
) # this is in meters
radius = radius / 1609
lat = call.data.get("location", {}).get("latitude", 0.0)
long = call.data.get("location", {}).get("longitude", 0.0)
lat = call.data.get("location", {}).get("latitude", default_lat)
long = call.data.get("location", {}).get("longitude", default_long)
fuel_type = call.data.get("type")
return {
"fuels": fuel_prices.find_fuel_from_point((lat, long), radius, fuel_type)
}
try:
return {
"fuels": await fuel_prices.find_fuel_from_point(
(lat, long), radius, fuel_type
)
}
except ValueError as err:
raise HomeAssistantError("Country not available for fuel data.") from err

def handle_fuel_location_lookup(call: ServiceCall) -> ServiceResponse:
async def handle_fuel_location_lookup(call: ServiceCall) -> ServiceResponse:
"""Handle a fuel location lookup call."""
radius = call.data.get("location", {}).get(
"radius", 8046.72
) # this is in meters
radius = radius / 1609
lat = call.data.get("location", {}).get("latitude", 0.0)
long = call.data.get("location", {}).get("longitude", 0.0)
location_ids = fuel_prices.find_fuel_locations_from_point((lat, long), radius)
locations = []
for loc_id in location_ids:
loc = fuel_prices.get_fuel_location(loc_id)
built = {
"name": loc.name,
"last_update": loc.last_updated,
"address": loc.address,
"latitude": loc.lat,
"longitude": loc.long,
"brand": loc.brand,
}
for fuel in loc.available_fuels:
built[fuel.fuel_type] = fuel.cost
locations.append(built)

return {"items": locations, "sources": entry.data.get("sources", [])}
lat = call.data.get("location", {}).get("latitude", default_lat)
long = call.data.get("location", {}).get("longitude", default_long)
try:
locations = await fuel_prices.find_fuel_locations_from_point(
(lat, long), radius
)
except ValueError as err:
raise HomeAssistantError("Country not available for fuel data.") from err
locations_built = []
for loc in locations:
await loc.dynamic_build_fuels()
locations_built.append(loc.__dict__())

return {"items": locations_built, "sources": entry.data.get("sources", [])}

hass.services.async_register(
DOMAIN,
Expand All @@ -105,6 +140,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Unload a config entry."""
_LOGGER.debug("Unloading config entry %s", entry.entry_id)
if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
await hass.data[DOMAIN][entry.entry_id].api.client_session.close()
hass.data[DOMAIN].pop(entry.entry_id)

return unload_ok
Expand Down
Loading

0 comments on commit b53ed84

Please sign in to comment.