meteocat 1.1.1 → 1.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,11 @@
1
+ ## [1.1.2](https://github.com/figorr/meteocat/compare/v1.1.1...v1.1.2) (2025-01-26)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * 2.0.0 ([8f9ad16](https://github.com/figorr/meteocat/commit/8f9ad169b92da1093c3cdb2ff64ad5ffae9c9b24))
7
+ * update installation methods at README ([e588ffa](https://github.com/figorr/meteocat/commit/e588ffa17c10652992a4c26d19d26b7757b051b5))
8
+
1
9
  ## [1.1.1](https://github.com/figorr/meteocat/compare/v1.1.0...v1.1.1) (2025-01-08)
2
10
 
3
11
 
package/README.md CHANGED
@@ -22,7 +22,17 @@ Authors:
22
22
 
23
23
  ## Installation
24
24
 
25
- #### HACS
25
+ #### HACS - Install using the custom repository method.
26
+
27
+ 1. First of all you need to add a custom repository like [this](https://hacs.xyz/docs/faq/custom_repositories/).
28
+ 1. Then download the integration from HACS.
29
+ 1. Restart Home Assistant.
30
+ 1. Go to `Settings > Devices & Services`
31
+ 1. Click `+ Add Integration`
32
+ 1. Search for `Meteocat` and follow the configuration instructions
33
+
34
+
35
+ #### HACS - Install from the store
26
36
  1. Go to `HACS`
27
37
  1. Search for `Meteocat` and add it to HACS
28
38
  1. Restart Home Assistant
@@ -44,7 +54,11 @@ After submitting your API Key you will either be prompted to pick a town from th
44
54
 
45
55
  Once you pick the town you will be prompted to pick a station from the list. These are the nearest stations to the picked town.
46
56
 
47
- ![Meteocat component station picker dialog](images/pick_station.png)
57
+ ![Meteocat custom component station picker dialog](images/pick_station.png)
58
+
59
+ Then you will be asked to set the XEMA and PREDICTIONS limits from the API.
60
+
61
+ ![Meteocat custom component api limits setting dialog](images/api_limits.png)
48
62
 
49
63
  Then you will be asked to pick an area for the device.
50
64
 
@@ -20,6 +20,8 @@ from .coordinator import (
20
20
  DailyForecastCoordinator,
21
21
  MeteocatConditionCoordinator,
22
22
  MeteocatTempForecastCoordinator,
23
+ MeteocatAlertsCoordinator,
24
+ MeteocatAlertsRegionCoordinator,
23
25
  )
24
26
 
25
27
  from .const import DOMAIN, PLATFORMS
@@ -27,7 +29,7 @@ from .const import DOMAIN, PLATFORMS
27
29
  _LOGGER = logging.getLogger(__name__)
28
30
 
29
31
  # Versión
30
- __version__ = "1.1.1"
32
+ __version__ = "2.0.0"
31
33
 
32
34
  # Definir el esquema de configuración CONFIG_SCHEMA
33
35
  CONFIG_SCHEMA = vol.Schema(
@@ -121,6 +123,12 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
121
123
  temp_forecast_coordinator = MeteocatTempForecastCoordinator(hass=hass, entry_data=entry_data)
122
124
  await temp_forecast_coordinator.async_config_entry_first_refresh()
123
125
 
126
+ alerts_coordinator = MeteocatAlertsCoordinator(hass=hass, entry_data=entry_data)
127
+ await alerts_coordinator.async_config_entry_first_refresh()
128
+
129
+ alerts_region_coordinator = MeteocatAlertsRegionCoordinator(hass=hass, entry_data=entry_data)
130
+ await alerts_region_coordinator.async_config_entry_first_refresh()
131
+
124
132
  except Exception as err: # Capturar todos los errores
125
133
  _LOGGER.exception(f"Error al inicializar los coordinadores: {err}")
126
134
  return False
@@ -137,6 +145,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
137
145
  "daily_forecast_coordinator": daily_forecast_coordinator,
138
146
  "condition_coordinator": condition_coordinator,
139
147
  "temp_forecast_coordinator": temp_forecast_coordinator,
148
+ "alerts_coordinator": alerts_coordinator,
149
+ "alerts_region_coordinator": alerts_region_coordinator,
140
150
  **entry_data,
141
151
  }
142
152
 
@@ -193,6 +203,16 @@ async def async_remove_entry(hass: HomeAssistant, entry: ConfigEntry) -> None:
193
203
  forecast_hourly_data_file = files_folder / f"forecast_{town_id.lower()}_hourly_data.json"
194
204
  forecast_daily_data_file = files_folder / f"forecast_{town_id.lower()}_daily_data.json"
195
205
 
206
+ # Obtener el `region_id` para identificar el archivo a eliminar
207
+ region_id = entry.data.get("region_id")
208
+ if not region_id:
209
+ _LOGGER.warning("No se encontró 'region_id' en la configuración. No se puede eliminar el archivo de alertas de la comarca.")
210
+ return
211
+
212
+ # Archivos JSON de alertas
213
+ alerts_file = files_folder / "alerts.json"
214
+ alerts_region_file = files_folder / f"alerts_{region_id}.json"
215
+
196
216
  # Validar la ruta base
197
217
  if not custom_components_path.exists():
198
218
  _LOGGER.warning(f"La ruta {custom_components_path} no existe. No se realizará la limpieza.")
@@ -205,5 +225,7 @@ async def async_remove_entry(hass: HomeAssistant, entry: ConfigEntry) -> None:
205
225
  safe_remove(town_data_file)
206
226
  safe_remove(forecast_hourly_data_file)
207
227
  safe_remove(forecast_daily_data_file)
228
+ safe_remove(alerts_file)
229
+ safe_remove(alerts_region_file)
208
230
  safe_remove(assets_folder, is_folder=True)
209
231
  safe_remove(files_folder, is_folder=True)
@@ -32,7 +32,9 @@ from .const import (
32
32
  REGION_NAME,
33
33
  PROVINCE_ID,
34
34
  PROVINCE_NAME,
35
- STATION_STATUS
35
+ STATION_STATUS,
36
+ LIMIT_XEMA,
37
+ LIMIT_PREDICCIO
36
38
  )
37
39
 
38
40
  from .options_flow import MeteocatOptionsFlowHandler
@@ -193,43 +195,20 @@ class MeteocatConfigFlow(ConfigFlow, domain=DOMAIN):
193
195
  infostation_client = MeteocatInfoStation(self.api_key)
194
196
  try:
195
197
  station_metadata = await infostation_client.get_infostation(self.station_id)
198
+ # Extraer los valores necesarios de los metadatos
199
+ self.station_type = station_metadata.get("tipus", "")
200
+ self.latitude = station_metadata.get("coordenades", {}).get("latitud", 0.0)
201
+ self.longitude = station_metadata.get("coordenades", {}).get("longitud", 0.0)
202
+ self.altitude = station_metadata.get("altitud", 0)
203
+ self.region_id = station_metadata.get("comarca", {}).get("codi", "")
204
+ self.region_name = station_metadata.get("comarca", {}).get("nom", "")
205
+ self.province_id = station_metadata.get("provincia", {}).get("codi", "")
206
+ self.province_name = station_metadata.get("provincia", {}).get("nom", "")
207
+ self.station_status = station_metadata.get("estats", [{}])[0].get("codi", "")
208
+ return await self.async_step_set_api_limits()
196
209
  except Exception as ex:
197
210
  _LOGGER.error("Error al obtener los metadatos de la estación: %s", ex)
198
211
  errors["base"] = "metadata_fetch_failed"
199
- station_metadata = {}
200
-
201
- # Extraer los valores necesarios de los metadatos
202
- self.station_type = station_metadata.get("tipus", "")
203
- self.latitude = station_metadata.get("coordenades", {}).get("latitud", 0.0)
204
- self.longitude = station_metadata.get("coordenades", {}).get("longitud", 0.0)
205
- self.altitude = station_metadata.get("altitud", 0)
206
- self.region_id = station_metadata.get("comarca", {}).get("codi", "")
207
- self.region_name = station_metadata.get("comarca", {}).get("nom", "")
208
- self.province_id = station_metadata.get("provincia", {}).get("codi", "")
209
- self.province_name = station_metadata.get("provincia", {}).get("nom", "")
210
- self.station_status = station_metadata.get("estats", [{}])[0].get("codi", "")
211
-
212
- return self.async_create_entry(
213
- title=self.selected_municipi["nom"],
214
- data={
215
- CONF_API_KEY: self.api_key,
216
- TOWN_NAME: self.selected_municipi["nom"],
217
- TOWN_ID: self.selected_municipi["codi"],
218
- VARIABLE_NAME: "Temperatura",
219
- VARIABLE_ID: str(self.variable_id),
220
- STATION_NAME: self.station_name,
221
- STATION_ID: self.station_id,
222
- STATION_TYPE: self.station_type,
223
- LATITUDE: self.latitude,
224
- LONGITUDE: self.longitude,
225
- ALTITUDE: self.altitude,
226
- REGION_ID: str(self.region_id),
227
- REGION_NAME: self.region_name,
228
- PROVINCE_ID: str(self.province_id),
229
- PROVINCE_NAME: self.province_name,
230
- STATION_STATUS: str(self.station_status),
231
- },
232
- )
233
212
  else:
234
213
  errors["base"] = "station_not_found"
235
214
 
@@ -244,6 +223,48 @@ class MeteocatConfigFlow(ConfigFlow, domain=DOMAIN):
244
223
  return self.async_show_form(
245
224
  step_id="select_station", data_schema=schema, errors=errors
246
225
  )
226
+
227
+ async def async_step_set_api_limits(self, user_input=None):
228
+ """Cuarto paso: Introducir los límites de XEMA y PREDICCIO del plan de la API."""
229
+ errors = {}
230
+
231
+ if user_input is not None:
232
+ self.limit_xema = user_input.get(LIMIT_XEMA, 750)
233
+ self.limit_prediccio = user_input.get(LIMIT_PREDICCIO, 100)
234
+ return self.async_create_entry(
235
+ title=self.selected_municipi["nom"],
236
+ data={
237
+ CONF_API_KEY: self.api_key,
238
+ TOWN_NAME: self.selected_municipi["nom"],
239
+ TOWN_ID: self.selected_municipi["codi"],
240
+ VARIABLE_NAME: "Temperatura",
241
+ VARIABLE_ID: str(self.variable_id),
242
+ STATION_NAME: self.station_name,
243
+ STATION_ID: self.station_id,
244
+ STATION_TYPE: self.station_type,
245
+ LATITUDE: self.latitude,
246
+ LONGITUDE: self.longitude,
247
+ ALTITUDE: self.altitude,
248
+ REGION_ID: str(self.region_id),
249
+ REGION_NAME: self.region_name,
250
+ PROVINCE_ID: str(self.province_id),
251
+ PROVINCE_NAME: self.province_name,
252
+ STATION_STATUS: str(self.station_status),
253
+ LIMIT_XEMA: self.limit_xema,
254
+ LIMIT_PREDICCIO: self.limit_prediccio,
255
+ },
256
+ )
257
+
258
+ schema = vol.Schema(
259
+ {
260
+ vol.Required(LIMIT_XEMA, default=750): cv.positive_int,
261
+ vol.Required(LIMIT_PREDICCIO, default=100): cv.positive_int,
262
+ }
263
+ )
264
+
265
+ return self.async_show_form(
266
+ step_id="set_api_limits", data_schema=schema, errors=errors
267
+ )
247
268
 
248
269
  @staticmethod
249
270
  @callback
@@ -16,10 +16,22 @@ REGION_ID = "region_id"
16
16
  REGION_NAME = "region_name"
17
17
  PROVINCE_ID = "province_id"
18
18
  PROVINCE_NAME = "province_name"
19
+ LIMIT_XEMA = "limit_xema"
20
+ LIMIT_PREDICCIO = "limit_prediccio"
19
21
  STATION_STATUS = "station_status"
20
22
  HOURLY_FORECAST_FILE_STATUS = "hourly_forecast_file_status"
21
23
  DAILY_FORECAST_FILE_STATUS = "daily_forecast_file_status"
22
24
  UVI_FILE_STATUS = "uvi_file_status"
25
+ ALERTS = "alerts"
26
+ ALERT_FILE_STATUS = "alert_file_status"
27
+ ALERT_WIND = "alert_wind"
28
+ ALERT_RAIN_INTENSITY = "alert_rain_intensity"
29
+ ALERT_RAIN = "alert_rain"
30
+ ALERT_SEA = "alert_sea"
31
+ ALERT_COLD = "alert_cold"
32
+ ALERT_WARM = "alert_warm"
33
+ ALERT_WARM_NIGHT = "alert_warm_night"
34
+ ALERT_SNOW = "alert_snow"
23
35
 
24
36
  from homeassistant.const import Platform
25
37
 
@@ -29,8 +41,15 @@ DEFAULT_NAME = "METEOCAT"
29
41
 
30
42
  # Tiempos para validación de API
31
43
  DEFAULT_VALIDITY_DAYS = 1 # Número de días a partir de los cuales se considera que el archivo de información está obsoleto
32
- DEFAULT_VALIDITY_HOURS = 5 # Hora a partir de la cuall la API tiene la información actualizada de predicciones disponible para descarga
33
- DEFAULT_VALIDITY_MINUTES = 0 # Minutos a partir de los cuales la API tiene la información actualizada de predicciones disponible para descarga
44
+ DEFAULT_VALIDITY_HOURS = 5 # Hora a partir de la cual la API tiene la información actualizada de predicciones disponible para descarga
45
+ DEFAULT_VALIDITY_MINUTES = 0 # Minutos a partir de los cuales la API tiene la información actualizada de predicciones disponible para descarga
46
+ DEFAULT_ALERT_VALIDITY_TIME = 120 # Minutos a partir de los cuales las alertas están obsoletas y se se debe proceder a una nueva llamada a la API
47
+
48
+ # Multiplicadores para la duración de validez basada en limit_prediccio
49
+ ALERT_VALIDITY_MULTIPLIER_100 = 12 # para limit_prediccio <= 100
50
+ ALERT_VALIDITY_MULTIPLIER_200 = 6 # para 100 < limit_prediccio <= 200
51
+ ALERT_VALIDITY_MULTIPLIER_500 = 3 # para 200 < limit_prediccio <= 500
52
+ ALERT_VALIDITY_MULTIPLIER_DEFAULT = 1 # para limit_prediccio > 500
34
53
 
35
54
  # Códigos de sensores de la API
36
55
  WIND_SPEED = "wind_speed" # Velocidad del viento