Skip to content

Commit ed5c831

Browse files
Create a new Config Flow with Stations List (#14)
* Create a new Config Flow with Stations List
1 parent 4ef949e commit ed5c831

File tree

7 files changed

+230
-76
lines changed

7 files changed

+230
-76
lines changed

custom_components/precoscombustiveis/__init__.py

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
from .dgeg import DGEG
1111
from .const import DOMAIN
1212

13-
__version__ = "1.1.0"
13+
__version__ = "2.0.0"
1414
_LOGGER = logging.getLogger(__name__)
1515

1616
PLATFORMS: list[str] = ["sensor"]
@@ -37,21 +37,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
3737
return True
3838

3939

40-
# async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry):
41-
# """Unload a config entry."""
42-
# unload_ok = await hass.config_entries.async_forward_entry_unload(
43-
# entry, DOMAIN)
44-
45-
# if unload_ok:
46-
# for unsub in hass.data[DOMAIN].listeners:
47-
# unsub()
48-
# hass.data.pop(DOMAIN)
49-
50-
# return True
51-
52-
# return False
53-
54-
5540
async def async_reload_entry(hass: HomeAssistant, entry: ConfigEntry) -> None:
5641
"""Reload config entry."""
5742
# await async_unload_entry(hass, entry)
Lines changed: 107 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,25 @@
11
"""Config flow for PrecosCombustiveis integration."""
22
from __future__ import annotations
3+
from typing import Any, Dict, Optional
4+
import voluptuous as vol
35

46
import logging
5-
import voluptuous as vol
67
import async_timeout
78

89
from homeassistant import config_entries
10+
from homeassistant.core import callback
11+
from homeassistant.data_entry_flow import FlowResult
912
from homeassistant.helpers.aiohttp_client import async_get_clientsession
1013

14+
from .const import (
15+
DOMAIN,
16+
CONF_STATIONID,
17+
CONF_STATION_NAME,
18+
CONF_STATION_BRAND,
19+
CONF_STATION_ADDRESS,
20+
DISTRITOS
21+
)
1122
from .dgeg import DGEG
12-
from .const import DOMAIN, CONF_STATIONID
1323

1424
_LOGGER = logging.getLogger(__name__)
1525

@@ -26,34 +36,99 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
2636
VERSION = 1
2737
CONNECTION_CLASS = config_entries.CONN_CLASS_CLOUD_POLL
2838

29-
async def async_step_user(self, user_input=None):
30-
"""Handle a flow initialized by the user interface."""
31-
_LOGGER.debug("Starting async_step_user...")
32-
errors = {}
33-
34-
if user_input is not None:
35-
await self.async_set_unique_id(user_input[CONF_STATIONID].lower())
36-
self._abort_if_unique_id_configured()
37-
38-
stationName = await self._test_gas_station(user_input[CONF_STATIONID])
39-
if stationName:
40-
_LOGGER.debug("Config is valid!")
41-
return self.async_create_entry(
42-
title=f"{user_input[CONF_STATIONID]}: {stationName}",
43-
data=user_input
44-
)
45-
else:
46-
errors = {"base": "invalid_station"}
47-
48-
return self.async_show_form(
49-
step_id="user",
50-
data_schema=DATA_SCHEMA,
51-
errors=errors,
52-
)
53-
54-
async def _test_gas_station(self, stationId):
55-
"""Return true if gas station exists."""
56-
session = async_get_clientsession(self.hass, True)
57-
async with async_timeout.timeout(10):
39+
def __init__(self):
40+
"""Initialize flow."""
41+
self._stations: list = []
42+
self._selected_station: Dict[str, Any] = {}
43+
self._distrito_id: int = None
44+
45+
async def async_step_user(
46+
self, user_input: Optional[Dict[str, Any]] = None
47+
) -> FlowResult:
48+
"""Handle the initial step - select distrito."""
49+
if user_input is None:
50+
# Create distrito selection list
51+
distritos_list = {
52+
str(id): name for id, name in DISTRITOS.items()
53+
}
54+
55+
return self.async_show_form(
56+
step_id="user",
57+
data_schema=vol.Schema({
58+
vol.Required("distrito_select"): vol.In(distritos_list)
59+
})
60+
)
61+
62+
# Store selected distrito and move to station selection
63+
self._distrito_id = int(user_input["distrito_select"])
64+
return await self.async_step_station()
65+
66+
async def async_step_station(
67+
self, user_input: Optional[Dict[str, Any]] = None
68+
) -> FlowResult:
69+
"""Handle station selection."""
70+
if user_input is None:
71+
# Fetch stations list for selected distrito
72+
session = async_get_clientsession(self.hass)
5873
api = DGEG(session)
59-
return await api.testStation(stationId)
74+
self._stations = await api.list_stations(self._distrito_id)
75+
76+
if not self._stations:
77+
return self.async_abort(reason="no_stations")
78+
79+
# Create selection list
80+
stations_list = {
81+
str(station["Id"]): f"{station['Localidade']}: {station['Marca']} - {station['Nome']}"
82+
for station in self._stations
83+
}
84+
85+
return self.async_show_form(
86+
step_id="station",
87+
data_schema=vol.Schema({
88+
vol.Required("station_select"): vol.In(stations_list)
89+
}),
90+
description_placeholders={
91+
"stations_count": str(len(self._stations)),
92+
"distrito": DISTRITOS[self._distrito_id]
93+
}
94+
)
95+
96+
# Store selected station details
97+
station_id = user_input["station_select"]
98+
selected_station = next(
99+
(station for station in self._stations if str(station["Id"]) == station_id),
100+
None
101+
)
102+
103+
if not selected_station:
104+
return self.async_abort(reason="station_not_found")
105+
106+
self._selected_station = {
107+
CONF_STATIONID: str(selected_station["Id"]),
108+
CONF_STATION_NAME: selected_station["Nome"],
109+
CONF_STATION_BRAND: selected_station["Marca"],
110+
CONF_STATION_ADDRESS: selected_station["Morada"],
111+
}
112+
113+
return await self.async_step_confirm()
114+
115+
async def async_step_confirm(
116+
self, user_input: Optional[Dict[str, Any]] = None
117+
) -> FlowResult:
118+
"""Confirm the station selection."""
119+
if user_input is None:
120+
return self.async_show_form(
121+
step_id="confirm",
122+
description_placeholders={
123+
"name": self._selected_station[CONF_STATION_NAME],
124+
"brand": self._selected_station[CONF_STATION_BRAND],
125+
"address": self._selected_station[CONF_STATION_ADDRESS],
126+
}
127+
)
128+
129+
# Create the config entry
130+
return self.async_create_entry(
131+
title=f"{self._selected_station[CONF_STATION_NAME]} - {self._selected_station[CONF_STATION_BRAND]}",
132+
data=self._selected_station,
133+
)
134+

custom_components/precoscombustiveis/const.py

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,33 @@
77
DEFAULT_ICON = "mdi:gas-station"
88
UNIT_OF_MEASUREMENT = "€"
99

10+
CONF_STATIONID = "stationId"
11+
12+
CONF_STATION_NAME = "station_name"
13+
CONF_STATION_BRAND = "station_brand"
14+
CONF_STATION_ADDRESS = "station_address"
15+
16+
# API endpoints
17+
API_STATIONS_LIST = "https://precoscombustiveis.dgeg.gov.pt/api/PrecoComb/PesquisarPostos?idDistrito={}&qtdPorPagina=99999&pagina=1"
1018
API_URI_TEMPLATE = "https://precoscombustiveis.dgeg.gov.pt/api/PrecoComb/GetDadosPosto?id={}"
1119

12-
CONF_STATIONID = "stationId"
20+
DISTRITOS = {
21+
1: "Aveiro",
22+
2: "Beja",
23+
3: "Braga",
24+
4: "Bragança",
25+
5: "Castelo Branco",
26+
6: "Coimbra",
27+
7: "Évora",
28+
8: "Faro",
29+
9: "Guarda",
30+
10: "Leiria",
31+
11: "Lisboa",
32+
12: "Portalegre",
33+
13: "Porto",
34+
14: "Santarém",
35+
15: "Setúbal",
36+
16: "Viana do Castelo",
37+
17: "Vila Real",
38+
18: "Viseu"
39+
}

custom_components/precoscombustiveis/dgeg.py

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,19 @@
11
"""API to DGEG."""
2-
import aiohttp
2+
from typing import Dict, Optional
3+
from datetime import datetime
4+
from aiohttp import ClientSession
35
import logging
46

57
from datetime import datetime
68

79
from .const import (
8-
API_URI_TEMPLATE
10+
API_URI_TEMPLATE,
11+
API_STATIONS_LIST,
12+
DISTRITOS
913
)
1014

1115
_LOGGER = logging.getLogger(__name__)
1216

13-
1417
class Station:
1518
"""Represents a STATION card."""
1619

@@ -80,6 +83,34 @@ class DGEG:
8083
def __init__(self, websession):
8184
self.websession = websession
8285

86+
async def list_stations(self, distrito_id: str) -> list[Dict]:
87+
"""Get list of all stations."""
88+
try:
89+
_LOGGER.debug(f"Fetching stations list for distrito Id:{distrito_id} ({DISTRITOS[distrito_id]})...")
90+
async with self.websession.get(
91+
API_STATIONS_LIST.format(distrito_id),
92+
headers={
93+
"Content-Type": "application/json"
94+
}
95+
) as res:
96+
if res.status == 200:
97+
json = await res.json()
98+
# Sort stations by name for better display
99+
return sorted(
100+
json['resultado'],
101+
key=lambda x: (
102+
x.get('Localidade', '').lower(),
103+
x.get('Marca', '').lower(),
104+
x.get('Nome', '').lower()
105+
)
106+
)
107+
else:
108+
_LOGGER.error("Failed to fetch stations list. Status: %s", res.status)
109+
return []
110+
except Exception as ex:
111+
_LOGGER.error("Error fetching stations list: %s", str(ex))
112+
return []
113+
83114
async def getStation(self, id: str) -> Station:
84115
"""Issue GAS STATION requests."""
85116
try:

custom_components/precoscombustiveis/strings.json

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,33 @@
33
"config": {
44
"step": {
55
"user": {
6-
"title": "Add Credentials (user)",
7-
"description": "Some description (user)",
6+
"title": "Select Your Location",
7+
"description": "Select the primary location for your gas station.",
88
"data": {
9-
"stationId": "StationId"
10-
},
11-
"data_description": {
12-
"stationId": "Identificador da Estação de Combustível"
9+
"distrito_select": "District"
1310
}
11+
},
12+
"station": {
13+
"title": "Select Gas Station",
14+
"description": "Found {stations_count} stations. Please select your station from the list.",
15+
"data": {
16+
"station_select": "Gas Station"
17+
}
18+
},
19+
"confirm": {
20+
"title": "Confirm Station Selection",
21+
"description": "Do you want to add the following station?\n\nName: {name}\nBrand: {brand}\nAddress: {address}"
1422
}
1523
},
1624
"error": {
17-
"invalid_station": "Invalid StationId or gas station not found."
25+
"cannot_connect": "Failed to connect",
26+
"invalid_station": "Invalid station ID",
27+
"unknown": "Unexpected error"
1828
},
1929
"abort": {
20-
"already_configured": "Integration is already configured"
30+
"no_stations": "No stations found. Please try again later.",
31+
"station_not_found": "Selected station not found",
32+
"already_configured": "This station is already configured"
2133
}
2234
}
2335
}

custom_components/precoscombustiveis/translations/en.json

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,33 @@
33
"config": {
44
"step": {
55
"user": {
6-
"title": "Add Gas Station",
7-
"description": "Please type the service station ID from which you want to obtain the prices. (Go to [dgeg](https://precoscombustiveis.dgeg.gov.pt/api/PrecoComb/ListarDadosPostos), search for the desired station and copy the `Id`)",
6+
"title": "Select Your Location",
7+
"description": "Select the primary location for your gas station.",
88
"data": {
9-
"stationId": "Station Id"
10-
},
11-
"data_description": {
12-
"stationId": "Station Id Identifier"
9+
"distrito_select": "Location"
1310
}
11+
},
12+
"station": {
13+
"title": "Select Gas Station",
14+
"description": "Found {stations_count} stations. Please select your station from the list.",
15+
"data": {
16+
"station_select": "Gas Station"
17+
}
18+
},
19+
"confirm": {
20+
"title": "Confirm Station Selection",
21+
"description": "Do you want to add the following station?\n\nName: {name}\nBrand: {brand}\nAddress: {address}"
1422
}
1523
},
1624
"error": {
17-
"invalid_station": "Invalid StationId or gas station not found."
25+
"cannot_connect": "Failed to connect",
26+
"invalid_station": "Invalid station ID",
27+
"unknown": "Unexpected error"
1828
},
1929
"abort": {
20-
"already_configured": "Integration is already configured"
30+
"no_stations": "No stations found. Please try again later.",
31+
"station_not_found": "Selected station not found",
32+
"already_configured": "This station is already configured"
2133
}
2234
}
2335
}

0 commit comments

Comments
 (0)