meteocat 0.1.35 → 0.1.36

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,20 @@
1
+ ## [0.1.36](https://github.com/figorr/meteocat/compare/v0.1.35...v0.1.36) (2024-12-17)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * 0.1.36 ([73da4ed](https://github.com/figorr/meteocat/commit/73da4ed7c598a150528294e89c93407e33abfd24))
7
+ * add hour attribute to UVI sensor ([a365626](https://github.com/figorr/meteocat/commit/a365626aa2bb585ca9a835afcec6cea99e85a4bd))
8
+ * add hour attribute translation ([d50f8bb](https://github.com/figorr/meteocat/commit/d50f8bbbcb3c8867dedcbe51f065951f5dfab817))
9
+ * fix self. _config.entry from deprecated self. config.entry ([ed4bfeb](https://github.com/figorr/meteocat/commit/ed4bfebf0ea45bc63de000a21f6063a0cb9e499a))
10
+ * ignore uvi test ([cf35867](https://github.com/figorr/meteocat/commit/cf358675b1afce3e1ea1650414a22c2372af4b49))
11
+ * set coordinators to uvi sensor ([38288cc](https://github.com/figorr/meteocat/commit/38288cc75a30a1ad77a492dcb2b15592379e774f))
12
+ * set uvi coordinators ([1ea0432](https://github.com/figorr/meteocat/commit/1ea0432d6749cac96d45e52d3cf18e7a83e59739))
13
+ * update devs ([9274984](https://github.com/figorr/meteocat/commit/9274984154facd81bdeaf0e0050c870a08996b10))
14
+ * update devs ([44d7699](https://github.com/figorr/meteocat/commit/44d7699c4d3bde13f6145a5380eafc5c77818c45))
15
+ * use cached data when API failed ([f31bd10](https://github.com/figorr/meteocat/commit/f31bd10539e77f7d3e12bc27b3bf88cc9ab317d2))
16
+ * uvi sensor ([ee0b194](https://github.com/figorr/meteocat/commit/ee0b19461bdfacc26d493f9af0e27729e45ae545))
17
+
1
18
  ## [0.1.35](https://github.com/figorr/meteocat/compare/v0.1.34...v0.1.35) (2024-12-14)
2
19
 
3
20
 
@@ -8,13 +8,19 @@ from homeassistant.core import HomeAssistant
8
8
  from homeassistant.exceptions import HomeAssistantError
9
9
  from homeassistant.helpers.entity_platform import async_get_platforms
10
10
 
11
- from .coordinator import MeteocatSensorCoordinator # , MeteocatEntityCoordinator
11
+ from .coordinator import (
12
+ MeteocatSensorCoordinator,
13
+ # MeteocatEntityCoordinator,
14
+ MeteocatUviCoordinator,
15
+ MeteocatUviFileCoordinator,
16
+ )
17
+
12
18
  from .const import DOMAIN, PLATFORMS
13
19
 
14
20
  _LOGGER = logging.getLogger(__name__)
15
21
 
16
22
  # Versión
17
- __version__ = "0.1.35"
23
+ __version__ = "0.1.36"
18
24
 
19
25
  def safe_remove(path: Path, is_folder: bool = False):
20
26
  """Elimina de forma segura un archivo o carpeta si existe."""
@@ -62,6 +68,13 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
62
68
 
63
69
  # entity_coordinator = MeteocatEntityCoordinator(hass=hass, entry_data=entry_data)
64
70
  # await entity_coordinator.async_config_entry_first_refresh()
71
+
72
+ uvi_coordinator = MeteocatUviCoordinator(hass=hass, entry_data=entry_data)
73
+ await uvi_coordinator.async_config_entry_first_refresh()
74
+
75
+ uvi_file_coordinator = MeteocatUviFileCoordinator(hass=hass, entry_data=entry_data)
76
+ await uvi_file_coordinator.async_config_entry_first_refresh()
77
+
65
78
  except Exception as err: # Capturar todos los errores
66
79
  _LOGGER.exception(f"Error al inicializar los coordinadores: {err}")
67
80
  return False
@@ -71,6 +84,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
71
84
  hass.data[DOMAIN][entry.entry_id] = {
72
85
  "sensor_coordinator": sensor_coordinator,
73
86
  # "entity_coordinator": entity_coordinator,
87
+ "uvi_coordinator": uvi_coordinator,
88
+ "uvi_file_coordinator": uvi_file_coordinator,
74
89
  **entry_data,
75
90
  }
76
91
 
@@ -114,6 +129,15 @@ async def async_remove_entry(hass: HomeAssistant, entry: ConfigEntry) -> None:
114
129
  # Archivo JSON de la estación
115
130
  station_data_file = files_folder / f"station_{station_id.lower()}_data.json"
116
131
 
132
+ # Obtener el `town_id` para identificar el archivo a eliminar
133
+ town_id = entry.data.get("town_id")
134
+ if not town_id:
135
+ _LOGGER.warning("No se encontró 'town_id' en la configuración. No se puede eliminar el archivo de datos de la estación.")
136
+ return
137
+
138
+ # Archivo JSON UVI del municipio
139
+ town_data_file = files_folder / f"uvi_{town_id.lower()}_data.json"
140
+
117
141
  # Validar la ruta base
118
142
  if not custom_components_path.exists():
119
143
  _LOGGER.warning(f"La ruta {custom_components_path} no existe. No se realizará la limpieza.")
@@ -123,5 +147,6 @@ async def async_remove_entry(hass: HomeAssistant, entry: ConfigEntry) -> None:
123
147
  safe_remove(symbols_file)
124
148
  safe_remove(variables_file)
125
149
  safe_remove(station_data_file)
150
+ safe_remove(town_data_file)
126
151
  safe_remove(assets_folder, is_folder=True)
127
152
  safe_remove(files_folder, is_folder=True)
@@ -5,7 +5,7 @@ import json
5
5
  import aiofiles
6
6
  import logging
7
7
  import asyncio
8
- from datetime import timedelta
8
+ from datetime import datetime, timedelta
9
9
  from typing import Dict, Any
10
10
 
11
11
  from homeassistant.core import HomeAssistant
@@ -13,7 +13,9 @@ from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
13
13
  from homeassistant.exceptions import ConfigEntryNotReady
14
14
 
15
15
  from meteocatpy.data import MeteocatStationData
16
+ from meteocatpy.uvi import MeteocatUviData
16
17
  # from meteocatpy.forecast import MeteocatForecast
18
+
17
19
  from meteocatpy.exceptions import (
18
20
  BadRequestError,
19
21
  ForbiddenError,
@@ -28,10 +30,14 @@ _LOGGER = logging.getLogger(__name__)
28
30
 
29
31
  # Valores predeterminados para los intervalos de actualización
30
32
  DEFAULT_SENSOR_UPDATE_INTERVAL = timedelta(minutes=90)
31
- DEFAULT_ENTITY_UPDATE_INTERVAL = timedelta(hours=12)
33
+ DEFAULT_ENTITY_UPDATE_INTERVAL = timedelta(hours=48)
34
+ DEFAULT_HOURLY_FORECAST_UPDATE_INTERVAL = timedelta(hours=24)
35
+ DEFAULT_DAYLY_FORECAST_UPDATE_INTERVAL = timedelta(hours=48)
36
+ DEFAULT_UVI_UPDATE_INTERVAL = timedelta(hours=48)
37
+ DEFAULT_UVI_SENSOR_UPDATE_INTERVAL = timedelta(minutes=5)
32
38
 
33
39
  async def save_json_to_file(data: dict, output_file: str) -> None:
34
- """Save the JSON data to a file asynchronously."""
40
+ """Guarda datos JSON en un archivo de forma asíncrona."""
35
41
  try:
36
42
  # Crea el directorio si no existe
37
43
  os.makedirs(os.path.dirname(output_file), exist_ok=True)
@@ -40,7 +46,17 @@ async def save_json_to_file(data: dict, output_file: str) -> None:
40
46
  async with aiofiles.open(output_file, mode="w", encoding="utf-8") as f:
41
47
  await f.write(json.dumps(data, indent=4, ensure_ascii=False))
42
48
  except Exception as e:
43
- raise RuntimeError(f"Error saving JSON to {output_file}: {e}")
49
+ raise RuntimeError(f"Error guardando JSON to {output_file}: {e}")
50
+
51
+ def load_json_from_file(input_file: str) -> dict | list | None:
52
+ """Carga datos JSON desde un archivo de forma síncrona."""
53
+ try:
54
+ if os.path.exists(input_file):
55
+ with open(input_file, "r", encoding="utf-8") as f:
56
+ return json.load(f)
57
+ except Exception as e:
58
+ _LOGGER.error(f"Error cargando JSON desde {input_file}: {e}")
59
+ return None
44
60
 
45
61
  class MeteocatSensorCoordinator(DataUpdateCoordinator):
46
62
  """Coordinator para manejar la actualización de datos de los sensores."""
@@ -132,12 +148,218 @@ class MeteocatSensorCoordinator(DataUpdateCoordinator):
132
148
  )
133
149
  raise
134
150
  except Exception as err:
135
- _LOGGER.exception(
136
- "Error inesperado al obtener datos de sensores (Station ID: %s): %s",
137
- self.station_id,
151
+ if isinstance(err, ConfigEntryNotReady):
152
+ # El dispositivo no pudo inicializarse por primera vez
153
+ _LOGGER.exception(
154
+ "No se pudo inicializar el dispositivo (Station ID: %s) debido a un error: %s",
155
+ self.station_id,
156
+ err,
157
+ )
158
+ raise # Re-raise the exception to indicate a fundamental failure in initialization
159
+ else:
160
+ # Manejar error durante la actualización de datos
161
+ _LOGGER.exception(
162
+ "Error inesperado al obtener datos de los sensores para (Station ID: %s): %s",
163
+ self.station_id,
164
+ err,
165
+ )
166
+ # Intentar cargar datos en caché si hay un error
167
+ cached_data = load_json_from_file(output_file)
168
+ if cached_data:
169
+ _LOGGER.info("Usando datos en caché para la estación %s.", self.station_id)
170
+ return cached_data
171
+ # No se puede actualizar el estado, retornar None o un estado fallido
172
+ return None # o cualquier otro valor que indique un estado de error
173
+
174
+ class MeteocatUviCoordinator(DataUpdateCoordinator):
175
+ """Coordinator para manejar la actualización de datos de los sensores."""
176
+
177
+ def __init__(
178
+ self,
179
+ hass: HomeAssistant,
180
+ entry_data: dict,
181
+ update_interval: timedelta = DEFAULT_UVI_UPDATE_INTERVAL,
182
+ ):
183
+ """
184
+ Inicializa el coordinador del sensor del Índice UV de Meteocat.
185
+
186
+ Args:
187
+ hass (HomeAssistant): Instancia de Home Assistant.
188
+ entry_data (dict): Datos de configuración obtenidos de core.config_entries.
189
+ update_interval (timedelta): Intervalo de actualización.
190
+ """
191
+ self.api_key = entry_data["api_key"] # Usamos la API key de la configuración
192
+ self.town_id = entry_data["town_id"] # Usamos el ID del municipio
193
+ self.meteocat_uvi_data = MeteocatUviData(self.api_key)
194
+
195
+ super().__init__(
196
+ hass,
197
+ _LOGGER,
198
+ name=f"{DOMAIN} Uvi Coordinator",
199
+ update_interval=update_interval,
200
+ )
201
+
202
+ async def _async_update_data(self) -> Dict:
203
+ """Actualiza los datos de los sensores desde la API de Meteocat."""
204
+ try:
205
+ # Obtener datos desde la API con manejo de tiempo límite
206
+ data = await asyncio.wait_for(
207
+ self.meteocat_uvi_data.get_uvi_index(self.town_id),
208
+ timeout=30 # Tiempo límite de 30 segundos
209
+ )
210
+ _LOGGER.debug("Datos de sensores actualizados exitosamente: %s", data)
211
+
212
+ # Validar que los datos sean un dict con una clave 'uvi'
213
+ if not isinstance(data, dict) or 'uvi' not in data:
214
+ _LOGGER.error("Formato inválido: Se esperaba un dict con la clave 'uvi'. Datos: %s", data)
215
+ raise ValueError("Formato de datos inválido")
216
+
217
+ # Extraer la lista de datos bajo la clave 'uvi'
218
+ uvi_data = data.get('uvi', [])
219
+
220
+ # Validar que 'uvi' sea una lista de diccionarios
221
+ if not isinstance(uvi_data, list) or not all(isinstance(item, dict) for item in uvi_data):
222
+ _LOGGER.error("Formato inválido: 'uvi' debe ser una lista de dicts. Datos: %s", uvi_data)
223
+ raise ValueError("Formato de datos inválido")
224
+
225
+ # Determinar la ruta al archivo en la carpeta raíz del repositorio
226
+ output_file = os.path.join(
227
+ self.hass.config.path(),
228
+ "custom_components",
229
+ "meteocat",
230
+ "files",
231
+ f"uvi_{self.town_id.lower()}_data.json"
232
+ )
233
+
234
+ # Guardar los datos en un archivo JSON
235
+ await save_json_to_file(data, output_file)
236
+
237
+ return uvi_data
238
+ except asyncio.TimeoutError as err:
239
+ _LOGGER.warning("Tiempo de espera agotado al obtener datos de la API de Meteocat.")
240
+ raise ConfigEntryNotReady from err
241
+ except ForbiddenError as err:
242
+ _LOGGER.error(
243
+ "Acceso denegado al obtener datos del índice UV para (Town ID: %s): %s",
244
+ self.town_id,
245
+ err,
246
+ )
247
+ raise ConfigEntryNotReady from err
248
+ except TooManyRequestsError as err:
249
+ _LOGGER.warning(
250
+ "Límite de solicitudes alcanzado al obtener datos del índice UV para (Town ID: %s): %s",
251
+ self.town_id,
252
+ err,
253
+ )
254
+ raise ConfigEntryNotReady from err
255
+ except (BadRequestError, InternalServerError, UnknownAPIError) as err:
256
+ _LOGGER.error(
257
+ "Error al obtener datos del índice UV para (Town ID: %s): %s",
258
+ self.town_id,
138
259
  err,
139
260
  )
140
261
  raise
262
+ except Exception as err:
263
+ if isinstance(err, ConfigEntryNotReady):
264
+ # El dispositivo no pudo inicializarse por primera vez
265
+ _LOGGER.exception(
266
+ "No se pudo inicializar el dispositivo (Town ID: %s) debido a un error: %s",
267
+ self.town_id,
268
+ err,
269
+ )
270
+ raise # Re-raise the exception to indicate a fundamental failure in initialization
271
+ else:
272
+ # Manejar error durante la actualización de datos
273
+ _LOGGER.exception(
274
+ "Error inesperado al obtener datos del índice UV para (Town ID: %s): %s",
275
+ self.town_id,
276
+ err,
277
+ )
278
+ # Intentar cargar datos en caché si hay un error
279
+ cached_data = load_json_from_file(output_file)
280
+ if cached_data:
281
+ _LOGGER.info("Usando datos en caché para la ciudad %s.", self.town_id)
282
+ return cached_data
283
+ # No se puede actualizar el estado, retornar None o un estado fallido
284
+ return None # o cualquier otro valor que indique un estado de error
285
+
286
+ class MeteocatUviFileCoordinator(DataUpdateCoordinator):
287
+ """Coordinator to read and process UV data from a file."""
288
+
289
+ def __init__(
290
+ self,
291
+ hass: HomeAssistant,
292
+ entry_data: dict,
293
+ update_interval: timedelta = DEFAULT_UVI_SENSOR_UPDATE_INTERVAL,
294
+ ):
295
+ """
296
+ Inicializa el coordinador del sensor del Índice UV de Meteocat.
297
+
298
+ Args:
299
+ hass (HomeAssistant): Instancia de Home Assistant.
300
+ entry_data (dict): Datos de configuración obtenidos de core.config_entries.
301
+ update_interval (timedelta): Intervalo de actualización.
302
+ """
303
+ self.town_id = entry_data["town_id"] # Usamos el ID del municipio
304
+
305
+ super().__init__(
306
+ hass,
307
+ _LOGGER,
308
+ name=f"{DOMAIN} Uvi File Coordinator",
309
+ update_interval=update_interval,
310
+ )
311
+ self._file_path = os.path.join(
312
+ hass.config.path("custom_components/meteocat/files"),
313
+ f"uvi_{self.town_id.lower()}_data.json",
314
+ )
315
+
316
+ async def _async_update_data(self):
317
+ """Read and process UV data for the current hour from the file asynchronously."""
318
+ try:
319
+ async with aiofiles.open(self._file_path, "r", encoding="utf-8") as file:
320
+ raw_data = await file.read()
321
+ raw_data = json.loads(raw_data) # Parse JSON data
322
+ except FileNotFoundError:
323
+ _LOGGER.error(
324
+ "No se ha encontrado el archivo JSON con datos del índice UV en %s.",
325
+ self._file_path,
326
+ )
327
+ return {}
328
+ except json.JSONDecodeError:
329
+ _LOGGER.error(
330
+ "Error al decodificar el archivo JSON del índice UV en %s.",
331
+ self._file_path,
332
+ )
333
+ return {}
334
+
335
+ return self._get_uv_for_current_hour(raw_data)
336
+
337
+ def _get_uv_for_current_hour(self, raw_data):
338
+ """Get UV data for the current hour."""
339
+ # Fecha y hora actual
340
+ current_datetime = datetime.now()
341
+ current_date = current_datetime.strftime("%Y-%m-%d")
342
+ current_hour = current_datetime.hour
343
+
344
+ # Busca los datos para la fecha actual
345
+ for day_data in raw_data.get("uvi", []):
346
+ if day_data["date"] == current_date:
347
+ # Encuentra los datos de la hora actual
348
+ for hour_data in day_data.get("hours", []):
349
+ if hour_data["hour"] == current_hour:
350
+ return {
351
+ "hour": hour_data.get("hour", 0),
352
+ "uvi": hour_data.get("uvi", 0),
353
+ "uvi_clouds": hour_data.get("uvi_clouds", 0),
354
+ }
355
+
356
+ # Si no se encuentran datos, devuelve un diccionario vacío con valores predeterminados
357
+ _LOGGER.warning(
358
+ "No se encontraron datos del índice UV para hoy (%s) y la hora actual (%s).",
359
+ current_date,
360
+ current_hour,
361
+ )
362
+ return {"hour": 0, "uvi": 0, "uvi_clouds": 0}
141
363
 
142
364
  # class MeteocatEntityCoordinator(DataUpdateCoordinator):
143
365
  # """Coordinator para manejar la actualización de datos de las entidades de predicción."""
@@ -8,5 +8,5 @@
8
8
  "documentation": "https://gitlab.com/figorr/meteocat",
9
9
  "loggers": ["meteocatpy"],
10
10
  "requirements": ["meteocatpy==0.0.15", "packaging>=20.3", "wrapt>=1.14.0"],
11
- "version": "0.1.35"
11
+ "version": "0.1.36"
12
12
  }
@@ -25,7 +25,7 @@ class MeteocatOptionsFlowHandler(OptionsFlow):
25
25
 
26
26
  def __init__(self, config_entry: ConfigEntry):
27
27
  """Inicializa el flujo de opciones."""
28
- self.config_entry = config_entry
28
+ self._config_entry = config_entry
29
29
  self.api_key: str | None = None
30
30
 
31
31
  async def async_step_init(self, user_input: dict | None = None):
@@ -60,8 +60,8 @@ class MeteocatOptionsFlowHandler(OptionsFlow):
60
60
  if not errors:
61
61
  # Actualizar la configuración de la entrada con la nueva API Key
62
62
  self.hass.config_entries.async_update_entry(
63
- self.config_entry,
64
- data={**self.config_entry.data, CONF_API_KEY: self.api_key},
63
+ self._config_entry,
64
+ data={**self._config_entry.data, CONF_API_KEY: self.api_key},
65
65
  )
66
66
  return self.async_create_entry(title="", data={})
67
67
 
@@ -59,7 +59,10 @@ from .const import (
59
59
  WIND_GUST_CODE,
60
60
  )
61
61
 
62
- from .coordinator import MeteocatSensorCoordinator
62
+ from .coordinator import (
63
+ MeteocatSensorCoordinator,
64
+ MeteocatUviFileCoordinator,
65
+ )
63
66
 
64
67
  _LOGGER = logging.getLogger(__name__)
65
68
 
@@ -201,18 +204,82 @@ SENSOR_TYPES: tuple[MeteocatSensorEntityDescription, ...] = (
201
204
  )
202
205
  )
203
206
 
204
-
205
207
  @callback
206
208
  async def async_setup_entry(hass, entry, async_add_entities: AddEntitiesCallback) -> None:
207
209
  """Set up Meteocat sensors from a config entry."""
208
210
  entry_data = hass.data[DOMAIN][entry.entry_id]
209
- coordinator = entry_data["sensor_coordinator"]
210
211
 
212
+ # Coordinadores para sensores
213
+ coordinator = entry_data.get("sensor_coordinator")
214
+ uvi_file_coordinator = entry_data.get("uvi_file_coordinator")
215
+
216
+ # Sensores generales
211
217
  async_add_entities(
212
218
  MeteocatSensor(coordinator, description, entry_data)
213
219
  for description in SENSOR_TYPES
220
+ if description.key != UV_INDEX # Excluir UVI del coordinador general
221
+ )
222
+
223
+ # Sensor UVI
224
+ async_add_entities(
225
+ MeteocatUviSensor(uvi_file_coordinator, description, entry_data)
226
+ for description in SENSOR_TYPES
227
+ if description.key == UV_INDEX # Incluir UVI en el coordinador UVI FILE COORDINATOR
214
228
  )
215
229
 
230
+ class MeteocatUviSensor(CoordinatorEntity[MeteocatUviFileCoordinator], SensorEntity):
231
+ """Representation of a Meteocat UV Index sensor."""
232
+
233
+ _attr_has_entity_name = True # Activa el uso de nombres basados en el dispositivo
234
+
235
+ def __init__(self, uvi_file_coordinator, description, entry_data):
236
+ """Initialize the UV Index sensor."""
237
+ super().__init__(uvi_file_coordinator)
238
+ self.entity_description = description
239
+ self._town_name = entry_data["town_name"]
240
+ self._town_id = entry_data["town_id"]
241
+ self._station_id = entry_data["station_id"]
242
+
243
+ # Unique ID for the entity
244
+ self._attr_unique_id = f"sensor.{DOMAIN}_{self._town_id}_{self.entity_description.key}"
245
+
246
+ # Asigna entity_category desde description (si está definido)
247
+ self._attr_entity_category = getattr(description, "entity_category", None)
248
+
249
+ # Log para depuración
250
+ _LOGGER.debug(
251
+ "Inicializando sensor: %s, Unique ID: %s",
252
+ self.entity_description.name,
253
+ self._attr_unique_id,
254
+ )
255
+
256
+ @property
257
+ def native_value(self):
258
+ """Return the current UV index value."""
259
+ if self.entity_description.key == UV_INDEX:
260
+ uvi_data = self.coordinator.data or {}
261
+ return uvi_data.get("uvi", None)
262
+
263
+ @property
264
+ def extra_state_attributes(self):
265
+ """Return additional attributes for the sensor."""
266
+ attributes = super().extra_state_attributes or {}
267
+ if self.entity_description.key == UV_INDEX:
268
+ uvi_data = self.coordinator.data or {}
269
+ # Add the "hour" attribute if it exists
270
+ attributes["hour"] = uvi_data.get("hour")
271
+ return attributes
272
+
273
+ @property
274
+ def device_info(self) -> DeviceInfo:
275
+ """Return the device info."""
276
+ return DeviceInfo(
277
+ identifiers={(DOMAIN, self._town_id)},
278
+ name="Meteocat " + self._station_id + " " + self._town_name,
279
+ manufacturer="Meteocat",
280
+ model="Meteocat API",
281
+ )
282
+
216
283
  class MeteocatSensor(CoordinatorEntity[MeteocatSensorCoordinator], SensorEntity):
217
284
  """Representation of a Meteocat sensor."""
218
285
  STATIC_KEYS = {TOWN_NAME, TOWN_ID, STATION_NAME, STATION_ID}
@@ -225,7 +292,7 @@ class MeteocatSensor(CoordinatorEntity[MeteocatSensorCoordinator], SensorEntity)
225
292
  PRESSURE: PRESSURE_CODE,
226
293
  PRECIPITATION: PRECIPITATION_CODE,
227
294
  SOLAR_GLOBAL_IRRADIANCE: SOLAR_GLOBAL_IRRADIANCE_CODE,
228
- UV_INDEX: UV_INDEX_CODE,
295
+ # UV_INDEX: UV_INDEX_CODE,
229
296
  MAX_TEMPERATURE: MAX_TEMPERATURE_CODE,
230
297
  MIN_TEMPERATURE: MIN_TEMPERATURE_CODE,
231
298
  WIND_GUST: WIND_GUST_CODE,
@@ -69,7 +69,10 @@
69
69
  "name": "Solar Global Irradiance"
70
70
  },
71
71
  "uv_index": {
72
- "name": "UV Index"
72
+ "name": "UV Index",
73
+ "attributes": {
74
+ "hour": "Hour"
75
+ }
73
76
  },
74
77
  "max_temperature": {
75
78
  "name": "Max Temperature"
@@ -69,7 +69,10 @@
69
69
  "name": "Irradiació Solar Global"
70
70
  },
71
71
  "uv_index": {
72
- "name": "UV Índex"
72
+ "name": "UV Índex",
73
+ "attributes": {
74
+ "hour": "Hora"
75
+ }
73
76
  },
74
77
  "max_temperature": {
75
78
  "name": "Temperatura Max"
@@ -69,7 +69,10 @@
69
69
  "name": "Solar Global Irradiance"
70
70
  },
71
71
  "uv_index": {
72
- "name": "UV Index"
72
+ "name": "UV Index",
73
+ "attributes": {
74
+ "hour": "Hour"
75
+ }
73
76
  },
74
77
  "max_temperature": {
75
78
  "name": "Max Temperature"
@@ -69,7 +69,10 @@
69
69
  "name": "Irradiación Solar Global"
70
70
  },
71
71
  "uv_index": {
72
- "name": "UV Índice"
72
+ "name": "UV Índice",
73
+ "attributes": {
74
+ "hour": "Hora"
75
+ }
73
76
  },
74
77
  "max_temperature": {
75
78
  "name": "Temperatura Max"
@@ -1,2 +1,2 @@
1
1
  # version.py
2
- __version__ = "0.1.35"
2
+ __version__ = "0.1.36"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "meteocat",
3
- "version": "0.1.35",
3
+ "version": "0.1.36",
4
4
  "description": "[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)\r [![Python version compatibility](https://img.shields.io/pypi/pyversions/meteocat)](https://pypi.org/project/meteocat)\r [![pipeline status](https://gitlab.com/figorr/meteocat/badges/master/pipeline.svg)](https://gitlab.com/figorr/meteocat/commits/master)",
5
5
  "main": "index.js",
6
6
  "directories": {
package/poetry.lock CHANGED
@@ -121,13 +121,13 @@ speedups = ["Brotli", "aiodns (>=3.2.0)", "brotlicffi"]
121
121
 
122
122
  [[package]]
123
123
  name = "aiosignal"
124
- version = "1.3.1"
124
+ version = "1.3.2"
125
125
  description = "aiosignal: a list of registered asynchronous callbacks"
126
126
  optional = false
127
- python-versions = ">=3.7"
127
+ python-versions = ">=3.9"
128
128
  files = [
129
- {file = "aiosignal-1.3.1-py3-none-any.whl", hash = "sha256:f8376fb07dd1e86a584e4fcdec80b36b7f81aac666ebc724e2c090300dd83b17"},
130
- {file = "aiosignal-1.3.1.tar.gz", hash = "sha256:54cd96e15e1649b75d6c87526a6ff0b6c1b0dd3459f43d9ca11d48c339b68cfc"},
129
+ {file = "aiosignal-1.3.2-py2.py3-none-any.whl", hash = "sha256:45cde58e409a301715980c2b01d0c28bdde3770d8290b5eb2173759d9acb31a5"},
130
+ {file = "aiosignal-1.3.2.tar.gz", hash = "sha256:a8c255c66fafb1e499c9351d0bf32ff2d8a0321595ebac3b93713656d2436f54"},
131
131
  ]
132
132
 
133
133
  [package.dependencies]
@@ -298,13 +298,13 @@ files = [
298
298
 
299
299
  [[package]]
300
300
  name = "certifi"
301
- version = "2024.8.30"
301
+ version = "2024.12.14"
302
302
  description = "Python package for providing Mozilla's CA Bundle."
303
303
  optional = false
304
304
  python-versions = ">=3.6"
305
305
  files = [
306
- {file = "certifi-2024.8.30-py3-none-any.whl", hash = "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8"},
307
- {file = "certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9"},
306
+ {file = "certifi-2024.12.14-py3-none-any.whl", hash = "sha256:1275f7a45be9464efc1173084eaa30f866fe2e47d389406136d332ed4967ec56"},
307
+ {file = "certifi-2024.12.14.tar.gz", hash = "sha256:b650d30f370c2b724812bee08008be0c4163b163ddaec3f2546c1caf65f191db"},
308
308
  ]
309
309
 
310
310
  [[package]]
@@ -796,13 +796,13 @@ poetry = ["poetry"]
796
796
 
797
797
  [[package]]
798
798
  name = "eventlet"
799
- version = "0.38.1"
799
+ version = "0.38.2"
800
800
  description = "Highly concurrent networking library"
801
801
  optional = false
802
802
  python-versions = ">=3.7"
803
803
  files = [
804
- {file = "eventlet-0.38.1-py3-none-any.whl", hash = "sha256:428eeff656bacf6d8c9a3d92fdbfe5a607c9e3e575ea70641de175aa9f3ec67a"},
805
- {file = "eventlet-0.38.1.tar.gz", hash = "sha256:ae23e3eb455facf0802df30c55da4ffbbf72e952757efa2197a1b47901255b1f"},
804
+ {file = "eventlet-0.38.2-py3-none-any.whl", hash = "sha256:4a2e3cbc53917c8f39074ccf689501168563d3a4df59e9cddd5e9d3b7f85c599"},
805
+ {file = "eventlet-0.38.2.tar.gz", hash = "sha256:6a46823af1dca7d29cf04c0d680365805435473c3acbffc176765c7f8787edac"},
806
806
  ]
807
807
 
808
808
  [package.dependencies]
@@ -2160,20 +2160,20 @@ testing = ["coverage (==6.2)", "mypy (==0.931)"]
2160
2160
 
2161
2161
  [[package]]
2162
2162
  name = "pytest-asyncio"
2163
- version = "0.24.0"
2163
+ version = "0.25.0"
2164
2164
  description = "Pytest support for asyncio"
2165
2165
  optional = false
2166
- python-versions = ">=3.8"
2166
+ python-versions = ">=3.9"
2167
2167
  files = [
2168
- {file = "pytest_asyncio-0.24.0-py3-none-any.whl", hash = "sha256:a811296ed596b69bf0b6f3dc40f83bcaf341b155a269052d82efa2b25ac7037b"},
2169
- {file = "pytest_asyncio-0.24.0.tar.gz", hash = "sha256:d081d828e576d85f875399194281e92bf8a68d60d72d1a2faf2feddb6c46b276"},
2168
+ {file = "pytest_asyncio-0.25.0-py3-none-any.whl", hash = "sha256:db5432d18eac6b7e28b46dcd9b69921b55c3b1086e85febfe04e70b18d9e81b3"},
2169
+ {file = "pytest_asyncio-0.25.0.tar.gz", hash = "sha256:8c0610303c9e0442a5db8604505fc0f545456ba1528824842b37b4a626cbf609"},
2170
2170
  ]
2171
2171
 
2172
2172
  [package.dependencies]
2173
2173
  pytest = ">=8.2,<9"
2174
2174
 
2175
2175
  [package.extras]
2176
- docs = ["sphinx (>=5.3)", "sphinx-rtd-theme (>=1.0)"]
2176
+ docs = ["sphinx (>=5.3)", "sphinx-rtd-theme (>=1)"]
2177
2177
  testing = ["coverage (>=6.2)", "hypothesis (>=5.7.1)"]
2178
2178
 
2179
2179
  [[package]]
package/pyproject.toml CHANGED
@@ -1,12 +1,15 @@
1
1
  [tool.poetry]
2
2
  name = "meteocat"
3
- version = "0.1.35"
3
+ version = "0.1.36"
4
4
  description = "Script para obtener datos meteorológicos de la API de Meteocat"
5
5
  authors = ["figorr <jdcuartero@yahoo.es>"]
6
6
  license = "Apache-2.0"
7
7
  readme = "README.md"
8
8
  repository = "https://gitlab.com/figorr/meteocat"
9
9
  keywords = ['meteocat']
10
+ packages = [
11
+ { include = "meteocat", from = "custom_components" }
12
+ ]
10
13
 
11
14
 
12
15
  [tool.poetry.dependencies]
@@ -62,4 +65,4 @@ pipdeptree = "^2.2.1"
62
65
 
63
66
  [tool.poetry.group.dev.dependencies]
64
67
  pyupgrade = "^3.4.0"
65
- pre-commit = "^3.3.1"
68
+ pre-commit = "^3.3.1"