meteocat 0.1.44 → 0.1.46
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 +19 -0
- package/custom_components/meteocat/__init__.py +1 -1
- package/custom_components/meteocat/const.py +4 -0
- package/custom_components/meteocat/coordinator.py +17 -10
- package/custom_components/meteocat/manifest.json +1 -1
- package/custom_components/meteocat/sensor.py +54 -15
- package/custom_components/meteocat/version.py +1 -1
- package/package.json +1 -1
- package/pyproject.toml +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,22 @@
|
|
|
1
|
+
## [0.1.46](https://github.com/figorr/meteocat/compare/v0.1.45...v0.1.46) (2025-01-01)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* 0.1.46 ([e2e736e](https://github.com/figorr/meteocat/commit/e2e736e102f25da704060bc6329696eec8c8fac6))
|
|
7
|
+
* add validity days, hours and minutes ([61a6761](https://github.com/figorr/meteocat/commit/61a6761cde31c321dbed4dce126416f65062a90f))
|
|
8
|
+
|
|
9
|
+
## [0.1.45](https://github.com/figorr/meteocat/compare/v0.1.44...v0.1.45) (2024-12-31)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
### Bug Fixes
|
|
13
|
+
|
|
14
|
+
* 0.1.45 ([ab522cf](https://github.com/figorr/meteocat/commit/ab522cf4d94687c64dc84480f8f5507645a79557))
|
|
15
|
+
* change icon for status sensors ([7415646](https://github.com/figorr/meteocat/commit/741564698b09b6e29b774bb388b6c96f718a573a))
|
|
16
|
+
* fix async_add_entities for generic dynamic sensors ([2192214](https://github.com/figorr/meteocat/commit/2192214ebb767e74c187df98e7c811225c21ca33))
|
|
17
|
+
* fix name and comments for Temp Forecast Coordinator ([e65e476](https://github.com/figorr/meteocat/commit/e65e476f03b0de062ab14a9563eaa71b6d80301f))
|
|
18
|
+
* fix name sensor coordinator ([e93d01f](https://github.com/figorr/meteocat/commit/e93d01f359158ee63494e697059550fcea37806f))
|
|
19
|
+
|
|
1
20
|
## [0.1.44](https://github.com/figorr/meteocat/compare/v0.1.43...v0.1.44) (2024-12-30)
|
|
2
21
|
|
|
3
22
|
|
|
@@ -25,7 +25,7 @@ from .const import DOMAIN, PLATFORMS
|
|
|
25
25
|
_LOGGER = logging.getLogger(__name__)
|
|
26
26
|
|
|
27
27
|
# Versión
|
|
28
|
-
__version__ = "0.1.
|
|
28
|
+
__version__ = "0.1.46"
|
|
29
29
|
|
|
30
30
|
def safe_remove(path: Path, is_folder: bool = False):
|
|
31
31
|
"""Elimina de forma segura un archivo o carpeta si existe."""
|
|
@@ -27,6 +27,10 @@ ATTRIBUTION = "Powered by Meteocatpy"
|
|
|
27
27
|
PLATFORMS = [Platform.SENSOR, Platform.WEATHER]
|
|
28
28
|
DEFAULT_NAME = "METEOCAT"
|
|
29
29
|
|
|
30
|
+
# Tiempos para validación de API
|
|
31
|
+
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
|
|
30
34
|
|
|
31
35
|
# Códigos de sensores de la API
|
|
32
36
|
WIND_SPEED = "wind_speed" # Velocidad del viento
|
|
@@ -5,7 +5,7 @@ import json
|
|
|
5
5
|
import aiofiles
|
|
6
6
|
import logging
|
|
7
7
|
import asyncio
|
|
8
|
-
from datetime import datetime, timedelta, timezone
|
|
8
|
+
from datetime import datetime, timedelta, timezone, time
|
|
9
9
|
from typing import Dict, Any
|
|
10
10
|
|
|
11
11
|
from homeassistant.core import HomeAssistant
|
|
@@ -29,6 +29,9 @@ from .condition import get_condition_from_statcel
|
|
|
29
29
|
from .const import (
|
|
30
30
|
DOMAIN,
|
|
31
31
|
CONDITION_MAPPING,
|
|
32
|
+
DEFAULT_VALIDITY_DAYS,
|
|
33
|
+
DEFAULT_VALIDITY_HOURS,
|
|
34
|
+
DEFAULT_VALIDITY_MINUTES,
|
|
32
35
|
)
|
|
33
36
|
|
|
34
37
|
_LOGGER = logging.getLogger(__name__)
|
|
@@ -285,6 +288,7 @@ class MeteocatUviCoordinator(DataUpdateCoordinator):
|
|
|
285
288
|
# Validar la fecha del primer elemento superior a 1 día
|
|
286
289
|
first_date = datetime.strptime(data["uvi"][0].get("date"), "%Y-%m-%d").date()
|
|
287
290
|
today = datetime.now(timezone.utc).date()
|
|
291
|
+
current_time = datetime.now(timezone.utc).time()
|
|
288
292
|
|
|
289
293
|
# Log detallado
|
|
290
294
|
_LOGGER.info(
|
|
@@ -292,10 +296,11 @@ class MeteocatUviCoordinator(DataUpdateCoordinator):
|
|
|
292
296
|
self.uvi_file,
|
|
293
297
|
today,
|
|
294
298
|
first_date,
|
|
299
|
+
current_time,
|
|
295
300
|
)
|
|
296
301
|
|
|
297
302
|
# Verificar si la antigüedad es mayor a un día
|
|
298
|
-
if (today - first_date).days >
|
|
303
|
+
if (today - first_date).days > DEFAULT_VALIDITY_DAYS and current_time >= time(DEFAULT_VALIDITY_HOURS, DEFAULT_VALIDITY_MINUTES):
|
|
299
304
|
_LOGGER.info(
|
|
300
305
|
"Los datos en %s son antiguos. Se procederá a llamar a la API.",
|
|
301
306
|
self.uvi_file,
|
|
@@ -508,6 +513,7 @@ class MeteocatEntityCoordinator(DataUpdateCoordinator):
|
|
|
508
513
|
# Obtener la fecha del primer día
|
|
509
514
|
first_date = datetime.fromisoformat(data["dies"][0]["data"].rstrip("Z")).date()
|
|
510
515
|
today = datetime.now(timezone.utc).date()
|
|
516
|
+
current_time = datetime.now(timezone.utc).time()
|
|
511
517
|
|
|
512
518
|
# Log detallado
|
|
513
519
|
_LOGGER.info(
|
|
@@ -515,10 +521,11 @@ class MeteocatEntityCoordinator(DataUpdateCoordinator):
|
|
|
515
521
|
file_path,
|
|
516
522
|
today,
|
|
517
523
|
first_date,
|
|
524
|
+
current_time,
|
|
518
525
|
)
|
|
519
526
|
|
|
520
527
|
# Verificar si la antigüedad es mayor a un día
|
|
521
|
-
if (today - first_date).days >
|
|
528
|
+
if (today - first_date).days > DEFAULT_VALIDITY_DAYS and current_time >= time(DEFAULT_VALIDITY_HOURS, DEFAULT_VALIDITY_MINUTES):
|
|
522
529
|
_LOGGER.info(
|
|
523
530
|
"Los datos en %s son antiguos. Se procederá a llamar a la API.",
|
|
524
531
|
file_path,
|
|
@@ -940,14 +947,14 @@ class MeteocatConditionCoordinator(DataUpdateCoordinator):
|
|
|
940
947
|
return {"condition": "unknown", "hour": current_hour, "icon": None, "date": current_date}
|
|
941
948
|
|
|
942
949
|
class MeteocatTempForecastCoordinator(DataUpdateCoordinator):
|
|
943
|
-
"""Coordinator para manejar las predicciones diarias desde archivos locales."""
|
|
950
|
+
"""Coordinator para manejar la temperatura máxima y mínima de las predicciones diarias desde archivos locales."""
|
|
944
951
|
|
|
945
952
|
def __init__(
|
|
946
953
|
self,
|
|
947
954
|
hass: HomeAssistant,
|
|
948
955
|
entry_data: dict,
|
|
949
956
|
):
|
|
950
|
-
"""Inicializa el coordinador para predicciones diarias."""
|
|
957
|
+
"""Inicializa el coordinador para las temperaturas máximas y mínimas de predicciones diarias."""
|
|
951
958
|
self.town_name = entry_data["town_name"]
|
|
952
959
|
self.town_id = entry_data["town_id"]
|
|
953
960
|
self.station_name = entry_data["station_name"]
|
|
@@ -962,7 +969,7 @@ class MeteocatTempForecastCoordinator(DataUpdateCoordinator):
|
|
|
962
969
|
super().__init__(
|
|
963
970
|
hass,
|
|
964
971
|
_LOGGER,
|
|
965
|
-
name=f"{DOMAIN} Daily Forecast Coordinator",
|
|
972
|
+
name=f"{DOMAIN} Daily Temperature Forecast Coordinator",
|
|
966
973
|
update_interval=DEFAULT_TEMP_FORECAST_UPDATE_INTERVAL,
|
|
967
974
|
)
|
|
968
975
|
|
|
@@ -1005,18 +1012,18 @@ class MeteocatTempForecastCoordinator(DataUpdateCoordinator):
|
|
|
1005
1012
|
if datetime.fromisoformat(dia["data"].rstrip("Z")).date() >= today
|
|
1006
1013
|
]
|
|
1007
1014
|
|
|
1008
|
-
# Usar datos del día actual si están disponibles
|
|
1015
|
+
# Usar datos de temperatura del día actual si están disponibles
|
|
1009
1016
|
today_temp_forecast = self.get_temp_forecast_for_today(data)
|
|
1010
1017
|
if today_temp_forecast:
|
|
1011
1018
|
parsed_data = self.parse_temp_forecast(today_temp_forecast)
|
|
1012
1019
|
return parsed_data
|
|
1013
1020
|
except Exception as e:
|
|
1014
|
-
_LOGGER.warning("Error leyendo archivo de predicción diaria: %s", e)
|
|
1021
|
+
_LOGGER.warning("Error leyendo temperaturas del archivo de predicción diaria: %s", e)
|
|
1015
1022
|
|
|
1016
1023
|
return {}
|
|
1017
1024
|
|
|
1018
1025
|
def get_temp_forecast_for_today(self, data: dict) -> dict | None:
|
|
1019
|
-
"""Obtiene los datos diarios para el día actual."""
|
|
1026
|
+
"""Obtiene los datos de temperaturas diarios para el día actual."""
|
|
1020
1027
|
if not data or "dies" not in data or not data["dies"]:
|
|
1021
1028
|
return None
|
|
1022
1029
|
|
|
@@ -1028,7 +1035,7 @@ class MeteocatTempForecastCoordinator(DataUpdateCoordinator):
|
|
|
1028
1035
|
return None
|
|
1029
1036
|
|
|
1030
1037
|
def parse_temp_forecast(self, dia: dict) -> dict:
|
|
1031
|
-
"""Convierte un día de predicción en un diccionario con los datos necesarios."""
|
|
1038
|
+
"""Convierte la temperatura de un día de predicción en un diccionario con los datos necesarios."""
|
|
1032
1039
|
variables = dia.get("variables", {})
|
|
1033
1040
|
|
|
1034
1041
|
temp_forecast_data = {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from dataclasses import dataclass
|
|
4
|
-
from datetime import datetime, timezone
|
|
4
|
+
from datetime import datetime, timezone, time
|
|
5
5
|
import logging
|
|
6
6
|
from homeassistant.helpers.entity import (
|
|
7
7
|
DeviceInfo,
|
|
@@ -63,6 +63,9 @@ from .const import (
|
|
|
63
63
|
MIN_TEMPERATURE_CODE,
|
|
64
64
|
FEELS_LIKE,
|
|
65
65
|
WIND_GUST_CODE,
|
|
66
|
+
DEFAULT_VALIDITY_DAYS,
|
|
67
|
+
DEFAULT_VALIDITY_HOURS,
|
|
68
|
+
DEFAULT_VALIDITY_MINUTES,
|
|
66
69
|
)
|
|
67
70
|
|
|
68
71
|
from .coordinator import (
|
|
@@ -237,19 +240,19 @@ SENSOR_TYPES: tuple[MeteocatSensorEntityDescription, ...] = (
|
|
|
237
240
|
MeteocatSensorEntityDescription(
|
|
238
241
|
key=HOURLY_FORECAST_FILE_STATUS,
|
|
239
242
|
translation_key="hourly_forecast_file_status",
|
|
240
|
-
icon="mdi:
|
|
243
|
+
icon="mdi:update",
|
|
241
244
|
entity_category=EntityCategory.DIAGNOSTIC,
|
|
242
245
|
),
|
|
243
246
|
MeteocatSensorEntityDescription(
|
|
244
247
|
key=DAILY_FORECAST_FILE_STATUS,
|
|
245
248
|
translation_key="daily_forecast_file_status",
|
|
246
|
-
icon="mdi:
|
|
249
|
+
icon="mdi:update",
|
|
247
250
|
entity_category=EntityCategory.DIAGNOSTIC,
|
|
248
251
|
),
|
|
249
252
|
MeteocatSensorEntityDescription(
|
|
250
253
|
key=UVI_FILE_STATUS,
|
|
251
254
|
translation_key="uvi_file_status",
|
|
252
|
-
icon="mdi:
|
|
255
|
+
icon="mdi:update",
|
|
253
256
|
entity_category=EntityCategory.DIAGNOSTIC,
|
|
254
257
|
)
|
|
255
258
|
)
|
|
@@ -260,7 +263,7 @@ async def async_setup_entry(hass, entry, async_add_entities: AddEntitiesCallback
|
|
|
260
263
|
entry_data = hass.data[DOMAIN][entry.entry_id]
|
|
261
264
|
|
|
262
265
|
# Coordinadores para sensores
|
|
263
|
-
|
|
266
|
+
sensor_coordinator = entry_data.get("sensor_coordinator")
|
|
264
267
|
uvi_file_coordinator = entry_data.get("uvi_file_coordinator")
|
|
265
268
|
static_sensor_coordinator = entry_data.get("static_sensor_coordinator")
|
|
266
269
|
condition_coordinator = entry_data.get("condition_coordinator")
|
|
@@ -270,9 +273,9 @@ async def async_setup_entry(hass, entry, async_add_entities: AddEntitiesCallback
|
|
|
270
273
|
|
|
271
274
|
# Sensores generales
|
|
272
275
|
async_add_entities(
|
|
273
|
-
MeteocatSensor(
|
|
276
|
+
MeteocatSensor(sensor_coordinator, description, entry_data)
|
|
274
277
|
for description in SENSOR_TYPES
|
|
275
|
-
if description.key
|
|
278
|
+
if description.key in {WIND_SPEED, WIND_DIRECTION, TEMPERATURE, HUMIDITY, PRESSURE, PRECIPITATION, PRECIPITATION_ACCUMULATED, SOLAR_GLOBAL_IRRADIANCE, MAX_TEMPERATURE, MIN_TEMPERATURE, FEELS_LIKE, WIND_GUST, STATION_TIMESTAMP} # Incluir sensores generales en el coordinador SENSOR COORDINATOR
|
|
276
279
|
)
|
|
277
280
|
|
|
278
281
|
# Sensores estáticos
|
|
@@ -502,9 +505,9 @@ class MeteocatSensor(CoordinatorEntity[MeteocatSensorCoordinator], SensorEntity)
|
|
|
502
505
|
|
|
503
506
|
_attr_has_entity_name = True # Activa el uso de nombres basados en el dispositivo
|
|
504
507
|
|
|
505
|
-
def __init__(self,
|
|
508
|
+
def __init__(self, sensor_coordinator, description, entry_data):
|
|
506
509
|
"""Initialize the sensor."""
|
|
507
|
-
super().__init__(
|
|
510
|
+
super().__init__(sensor_coordinator)
|
|
508
511
|
self.entity_description = description
|
|
509
512
|
self.api_key = entry_data["api_key"]
|
|
510
513
|
self._town_name = entry_data["town_name"]
|
|
@@ -793,9 +796,21 @@ class MeteocatHourlyForecastStatusSensor(CoordinatorEntity[MeteocatEntityCoordin
|
|
|
793
796
|
first_date = self._get_first_date()
|
|
794
797
|
if first_date:
|
|
795
798
|
today = datetime.now(timezone.utc).date()
|
|
799
|
+
current_time = datetime.now(timezone.utc).time()
|
|
796
800
|
days_difference = (today - first_date).days
|
|
797
|
-
_LOGGER.debug(
|
|
798
|
-
|
|
801
|
+
_LOGGER.debug(
|
|
802
|
+
f"Diferencia de días para predicciones horarias: {days_difference}."
|
|
803
|
+
f"Hora actual de validación: {current_time}."
|
|
804
|
+
f"Para la validación: "
|
|
805
|
+
f"número de días= {DEFAULT_VALIDITY_DAYS}, "
|
|
806
|
+
f"hora de contacto a la API >= {DEFAULT_VALIDITY_HOURS}, "
|
|
807
|
+
f"minutos de contacto a la API >= {DEFAULT_VALIDITY_MINUTES}."
|
|
808
|
+
)
|
|
809
|
+
|
|
810
|
+
# Validar fecha y hora según la lógica del coordinador
|
|
811
|
+
if days_difference > DEFAULT_VALIDITY_DAYS and current_time >= time(DEFAULT_VALIDITY_HOURS, DEFAULT_VALIDITY_MINUTES):
|
|
812
|
+
return "obsolete"
|
|
813
|
+
return "updated"
|
|
799
814
|
return "unknown"
|
|
800
815
|
|
|
801
816
|
@property
|
|
@@ -844,9 +859,21 @@ class MeteocatDailyForecastStatusSensor(CoordinatorEntity[MeteocatEntityCoordina
|
|
|
844
859
|
first_date = self._get_first_date()
|
|
845
860
|
if first_date:
|
|
846
861
|
today = datetime.now(timezone.utc).date()
|
|
862
|
+
current_time = datetime.now(timezone.utc).time()
|
|
847
863
|
days_difference = (today - first_date).days
|
|
848
|
-
_LOGGER.debug(
|
|
849
|
-
|
|
864
|
+
_LOGGER.debug(
|
|
865
|
+
f"Diferencia de días para predicciones diarias: {days_difference}."
|
|
866
|
+
f"Hora actual de validación: {current_time}."
|
|
867
|
+
f"Para la validación: "
|
|
868
|
+
f"número de días= {DEFAULT_VALIDITY_DAYS}, "
|
|
869
|
+
f"hora de contacto a la API >= {DEFAULT_VALIDITY_HOURS}, "
|
|
870
|
+
f"minutos de contacto a la API >= {DEFAULT_VALIDITY_MINUTES}."
|
|
871
|
+
)
|
|
872
|
+
|
|
873
|
+
# Validar fecha y hora según la lógica del coordinador
|
|
874
|
+
if days_difference > DEFAULT_VALIDITY_DAYS and current_time >= time(DEFAULT_VALIDITY_HOURS, DEFAULT_VALIDITY_MINUTES):
|
|
875
|
+
return "obsolete"
|
|
876
|
+
return "updated"
|
|
850
877
|
return "unknown"
|
|
851
878
|
|
|
852
879
|
@property
|
|
@@ -894,9 +921,21 @@ class MeteocatUviStatusSensor(CoordinatorEntity[MeteocatUviCoordinator], SensorE
|
|
|
894
921
|
first_date = self._get_first_date()
|
|
895
922
|
if first_date:
|
|
896
923
|
today = datetime.now(timezone.utc).date()
|
|
924
|
+
current_time = datetime.now(timezone.utc).time()
|
|
897
925
|
days_difference = (today - first_date).days
|
|
898
|
-
_LOGGER.debug(
|
|
899
|
-
|
|
926
|
+
_LOGGER.debug(
|
|
927
|
+
f"Diferencia de días para datos UVI: {days_difference}."
|
|
928
|
+
f"Hora actual de validación: {current_time}."
|
|
929
|
+
f"Para la validación: "
|
|
930
|
+
f"número de días= {DEFAULT_VALIDITY_DAYS}, "
|
|
931
|
+
f"hora de contacto a la API >= {DEFAULT_VALIDITY_HOURS}, "
|
|
932
|
+
f"minutos de contacto a la API >= {DEFAULT_VALIDITY_MINUTES}."
|
|
933
|
+
)
|
|
934
|
+
|
|
935
|
+
# Validar fecha y hora según la lógica del coordinador
|
|
936
|
+
if days_difference > DEFAULT_VALIDITY_DAYS and current_time >= time(DEFAULT_VALIDITY_HOURS, DEFAULT_VALIDITY_MINUTES):
|
|
937
|
+
return "obsolete"
|
|
938
|
+
return "updated"
|
|
900
939
|
return "unknown"
|
|
901
940
|
|
|
902
941
|
@property
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
# version.py
|
|
2
|
-
__version__ = "0.1.
|
|
2
|
+
__version__ = "0.1.46"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "meteocat",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.46",
|
|
4
4
|
"description": "[](https://opensource.org/licenses/Apache-2.0)\r [](https://pypi.org/project/meteocat)\r [](https://gitlab.com/figorr/meteocat/commits/master)",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"directories": {
|