meteocat 0.1.45 → 0.1.47
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/.github/workflows/release.yml +7 -1
- package/CHANGELOG.md +18 -0
- package/custom_components/meteocat/__init__.py +1 -1
- package/custom_components/meteocat/const.py +4 -0
- package/custom_components/meteocat/coordinator.py +73 -32
- package/custom_components/meteocat/manifest.json +1 -1
- package/custom_components/meteocat/sensor.py +75 -8
- package/custom_components/meteocat/version.py +1 -1
- package/package.json +1 -1
- package/pyproject.toml +1 -1
|
@@ -24,8 +24,14 @@ jobs:
|
|
|
24
24
|
# Paso 3: Instalar dependencias necesarias
|
|
25
25
|
- name: Install dependencies
|
|
26
26
|
run: npm ci
|
|
27
|
+
|
|
28
|
+
# Paso 4: Configurar el autor de Git
|
|
29
|
+
- name: Configure Git author
|
|
30
|
+
run: |
|
|
31
|
+
git config user.name "semantic-release-bot"
|
|
32
|
+
git config user.email "jdcuartero@yahoo.es"
|
|
27
33
|
|
|
28
|
-
# Paso
|
|
34
|
+
# Paso 5: Ejecutar semantic-release
|
|
29
35
|
- name: Run semantic-release
|
|
30
36
|
env:
|
|
31
37
|
GITHUB_TOKEN: ${{ secrets.WORKFLOW_TOKEN }}
|
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,21 @@
|
|
|
1
|
+
## [0.1.47](https://github.com/figorr/meteocat/compare/v0.1.46...v0.1.47) (2025-01-02)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* 0.1.47 ([03d1d08](https://github.com/figorr/meteocat/commit/03d1d08dd837dc47d8f2062ba561be706e1d8b05))
|
|
7
|
+
* convert to local time from UTC ([0dfe9f9](https://github.com/figorr/meteocat/commit/0dfe9f9ef7409bcd23d839963076aff14921f114))
|
|
8
|
+
* fix semantic-release-bot ([754f18b](https://github.com/figorr/meteocat/commit/754f18b2c0378c704eb255259192655b76100c43))
|
|
9
|
+
* fix timestamp sensor from UTC to local time Europe/Madrid ([e64539a](https://github.com/figorr/meteocat/commit/e64539a091adecfd4e23419c63bc54eda2293da3))
|
|
10
|
+
|
|
11
|
+
## [0.1.46](https://github.com/figorr/meteocat/compare/v0.1.45...v0.1.46) (2025-01-01)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
### Bug Fixes
|
|
15
|
+
|
|
16
|
+
* 0.1.46 ([e2e736e](https://github.com/figorr/meteocat/commit/e2e736e102f25da704060bc6329696eec8c8fac6))
|
|
17
|
+
* add validity days, hours and minutes ([61a6761](https://github.com/figorr/meteocat/commit/61a6761cde31c321dbed4dce126416f65062a90f))
|
|
18
|
+
|
|
1
19
|
## [0.1.45](https://github.com/figorr/meteocat/compare/v0.1.44...v0.1.45) (2024-12-31)
|
|
2
20
|
|
|
3
21
|
|
|
@@ -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.47"
|
|
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,8 @@ 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
|
+
from zoneinfo import ZoneInfo
|
|
9
10
|
from typing import Dict, Any
|
|
10
11
|
|
|
11
12
|
from homeassistant.core import HomeAssistant
|
|
@@ -29,6 +30,9 @@ from .condition import get_condition_from_statcel
|
|
|
29
30
|
from .const import (
|
|
30
31
|
DOMAIN,
|
|
31
32
|
CONDITION_MAPPING,
|
|
33
|
+
DEFAULT_VALIDITY_DAYS,
|
|
34
|
+
DEFAULT_VALIDITY_HOURS,
|
|
35
|
+
DEFAULT_VALIDITY_MINUTES,
|
|
32
36
|
)
|
|
33
37
|
|
|
34
38
|
_LOGGER = logging.getLogger(__name__)
|
|
@@ -77,6 +81,30 @@ async def load_json_from_file(input_file: str) -> dict:
|
|
|
77
81
|
_LOGGER.error("Error al decodificar JSON del archivo %s: %s", input_file, err)
|
|
78
82
|
return {}
|
|
79
83
|
|
|
84
|
+
# Cambiar UTC a la zona horaria local
|
|
85
|
+
def convert_to_local_time(utc_time: str, local_tz: str = "Europe/Madrid") -> datetime | None:
|
|
86
|
+
"""
|
|
87
|
+
Convierte una fecha/hora UTC en formato ISO 8601 a la zona horaria local especificada.
|
|
88
|
+
|
|
89
|
+
Args:
|
|
90
|
+
utc_time (str): Fecha/hora en formato ISO 8601 (ejemplo: '2025-01-02T12:00:00Z').
|
|
91
|
+
local_tz (str): Zona horaria local en formato IANA (por defecto, 'Europe/Madrid').
|
|
92
|
+
|
|
93
|
+
Returns:
|
|
94
|
+
datetime | None: Objeto datetime convertido a la zona horaria local, o None si hay un error.
|
|
95
|
+
"""
|
|
96
|
+
try:
|
|
97
|
+
utc_dt = datetime.fromisoformat(utc_time.replace("Z", "+00:00"))
|
|
98
|
+
local_dt = utc_dt.replace(tzinfo=ZoneInfo("UTC")).astimezone(ZoneInfo(local_tz))
|
|
99
|
+
return local_dt
|
|
100
|
+
except ValueError:
|
|
101
|
+
return None
|
|
102
|
+
|
|
103
|
+
# Obtener la hora local en lugar de la hora UTC
|
|
104
|
+
def get_local_datetime(tz: str = "Europe/Madrid"):
|
|
105
|
+
"""Obtiene la fecha y hora local según la zona horaria proporcionada."""
|
|
106
|
+
return datetime.now(ZoneInfo(tz))
|
|
107
|
+
|
|
80
108
|
class MeteocatSensorCoordinator(DataUpdateCoordinator):
|
|
81
109
|
"""Coordinator para manejar la actualización de datos de los sensores."""
|
|
82
110
|
|
|
@@ -283,8 +311,9 @@ class MeteocatUviCoordinator(DataUpdateCoordinator):
|
|
|
283
311
|
data = json.loads(content)
|
|
284
312
|
|
|
285
313
|
# Validar la fecha del primer elemento superior a 1 día
|
|
286
|
-
first_date =
|
|
287
|
-
today =
|
|
314
|
+
first_date = convert_to_local_time(data["uvi"][0].get("date"), "%Y-%m-%d").date()
|
|
315
|
+
today = get_local_datetime().date() # Usar la hora local
|
|
316
|
+
current_time = get_local_datetime().time()
|
|
288
317
|
|
|
289
318
|
# Log detallado
|
|
290
319
|
_LOGGER.info(
|
|
@@ -292,10 +321,11 @@ class MeteocatUviCoordinator(DataUpdateCoordinator):
|
|
|
292
321
|
self.uvi_file,
|
|
293
322
|
today,
|
|
294
323
|
first_date,
|
|
324
|
+
current_time,
|
|
295
325
|
)
|
|
296
326
|
|
|
297
327
|
# Verificar si la antigüedad es mayor a un día
|
|
298
|
-
if (today - first_date).days >
|
|
328
|
+
if (today - first_date).days > DEFAULT_VALIDITY_DAYS and current_time >= time(DEFAULT_VALIDITY_HOURS, DEFAULT_VALIDITY_MINUTES):
|
|
299
329
|
_LOGGER.info(
|
|
300
330
|
"Los datos en %s son antiguos. Se procederá a llamar a la API.",
|
|
301
331
|
self.uvi_file,
|
|
@@ -423,8 +453,8 @@ class MeteocatUviFileCoordinator(DataUpdateCoordinator):
|
|
|
423
453
|
|
|
424
454
|
def _get_uv_for_current_hour(self, raw_data):
|
|
425
455
|
"""Get UV data for the current hour."""
|
|
426
|
-
#
|
|
427
|
-
current_datetime =
|
|
456
|
+
# # Obtiene la fecha y hora locales
|
|
457
|
+
current_datetime = get_local_datetime()
|
|
428
458
|
current_date = current_datetime.strftime("%Y-%m-%d")
|
|
429
459
|
current_hour = current_datetime.hour
|
|
430
460
|
|
|
@@ -505,9 +535,10 @@ class MeteocatEntityCoordinator(DataUpdateCoordinator):
|
|
|
505
535
|
content = await f.read()
|
|
506
536
|
data = json.loads(content)
|
|
507
537
|
|
|
508
|
-
#
|
|
509
|
-
first_date =
|
|
510
|
-
today =
|
|
538
|
+
# Convertir la fecha del primer día a la zona horaria local
|
|
539
|
+
first_date = convert_to_local_time(data["dies"][0]["data"]).date()
|
|
540
|
+
today = get_local_datetime().date() # Usar la hora local
|
|
541
|
+
current_time = get_local_datetime().time()
|
|
511
542
|
|
|
512
543
|
# Log detallado
|
|
513
544
|
_LOGGER.info(
|
|
@@ -515,10 +546,11 @@ class MeteocatEntityCoordinator(DataUpdateCoordinator):
|
|
|
515
546
|
file_path,
|
|
516
547
|
today,
|
|
517
548
|
first_date,
|
|
549
|
+
current_time,
|
|
518
550
|
)
|
|
519
551
|
|
|
520
552
|
# Verificar si la antigüedad es mayor a un día
|
|
521
|
-
if (today - first_date).days >
|
|
553
|
+
if (today - first_date).days > DEFAULT_VALIDITY_DAYS and current_time >= time(DEFAULT_VALIDITY_HOURS, DEFAULT_VALIDITY_MINUTES):
|
|
522
554
|
_LOGGER.info(
|
|
523
555
|
"Los datos en %s son antiguos. Se procederá a llamar a la API.",
|
|
524
556
|
file_path,
|
|
@@ -545,6 +577,11 @@ class MeteocatEntityCoordinator(DataUpdateCoordinator):
|
|
|
545
577
|
hourly_data = await self._fetch_and_save_data(
|
|
546
578
|
self.meteocat_forecast.get_prediccion_horaria, self.hourly_file
|
|
547
579
|
)
|
|
580
|
+
|
|
581
|
+
# Convertir las fechas horarias a hora local
|
|
582
|
+
for day in hourly_data.get("dies", []):
|
|
583
|
+
for hour_data in day["variables"].get("temp", {}).get("valors", []):
|
|
584
|
+
hour_data["data"] = convert_to_local_time(hour_data["data"]).isoformat()
|
|
548
585
|
|
|
549
586
|
# Validar o actualizar datos diarios
|
|
550
587
|
daily_data = await self.validate_forecast_data(self.daily_file)
|
|
@@ -553,6 +590,10 @@ class MeteocatEntityCoordinator(DataUpdateCoordinator):
|
|
|
553
590
|
self.meteocat_forecast.get_prediccion_diaria, self.daily_file
|
|
554
591
|
)
|
|
555
592
|
|
|
593
|
+
# Convertir las fechas diarias a hora local
|
|
594
|
+
for day in daily_data.get("dies", []):
|
|
595
|
+
day["data"] = convert_to_local_time(day["data"]).isoformat()
|
|
596
|
+
|
|
556
597
|
return {"hourly": hourly_data, "daily": daily_data}
|
|
557
598
|
|
|
558
599
|
except asyncio.TimeoutError as err:
|
|
@@ -639,10 +680,10 @@ class HourlyForecastCoordinator(DataUpdateCoordinator):
|
|
|
639
680
|
if not data or "dies" not in data:
|
|
640
681
|
return False
|
|
641
682
|
|
|
642
|
-
now =
|
|
683
|
+
now = get_local_datetime()
|
|
643
684
|
for dia in data["dies"]:
|
|
644
685
|
for forecast in dia.get("variables", {}).get("estatCel", {}).get("valors", []):
|
|
645
|
-
forecast_time =
|
|
686
|
+
forecast_time = convert_to_local_time(forecast["data"])
|
|
646
687
|
if forecast_time >= now:
|
|
647
688
|
return True
|
|
648
689
|
|
|
@@ -668,7 +709,7 @@ class HourlyForecastCoordinator(DataUpdateCoordinator):
|
|
|
668
709
|
variables = dia.get("variables", {})
|
|
669
710
|
condition_code = next(
|
|
670
711
|
(item["valor"] for item in variables.get("estatCel", {}).get("valors", []) if
|
|
671
|
-
|
|
712
|
+
convert_to_local_time(item["data"]) == forecast_time),
|
|
672
713
|
-1,
|
|
673
714
|
)
|
|
674
715
|
|
|
@@ -697,10 +738,10 @@ class HourlyForecastCoordinator(DataUpdateCoordinator):
|
|
|
697
738
|
return []
|
|
698
739
|
|
|
699
740
|
forecasts = []
|
|
700
|
-
now =
|
|
741
|
+
now = get_local_datetime()
|
|
701
742
|
for dia in self.data["dies"]:
|
|
702
743
|
for forecast in dia.get("variables", {}).get("estatCel", {}).get("valors", []):
|
|
703
|
-
forecast_time =
|
|
744
|
+
forecast_time = convert_to_local_time(forecast["data"])
|
|
704
745
|
if forecast_time >= now:
|
|
705
746
|
forecasts.append(self.parse_hourly_forecast(dia, forecast_time))
|
|
706
747
|
return forecasts
|
|
@@ -720,7 +761,7 @@ class HourlyForecastCoordinator(DataUpdateCoordinator):
|
|
|
720
761
|
|
|
721
762
|
for valor in valores:
|
|
722
763
|
try:
|
|
723
|
-
data_hora =
|
|
764
|
+
data_hora = convert_to_local_time(valor["data"])
|
|
724
765
|
if data_hora == target_time:
|
|
725
766
|
return float(valor["valor"])
|
|
726
767
|
except (KeyError, ValueError) as e:
|
|
@@ -770,9 +811,9 @@ class DailyForecastCoordinator(DataUpdateCoordinator):
|
|
|
770
811
|
if not data or "dies" not in data or not data["dies"]:
|
|
771
812
|
return False
|
|
772
813
|
|
|
773
|
-
today =
|
|
814
|
+
today = get_local_datetime().date() # Usar la hora local
|
|
774
815
|
for dia in data["dies"]:
|
|
775
|
-
forecast_date =
|
|
816
|
+
forecast_date = convert_to_local_time(dia["data"], "Europe/Madrid").date()
|
|
776
817
|
if forecast_date >= today:
|
|
777
818
|
return True
|
|
778
819
|
|
|
@@ -790,10 +831,10 @@ class DailyForecastCoordinator(DataUpdateCoordinator):
|
|
|
790
831
|
data = json.loads(content)
|
|
791
832
|
|
|
792
833
|
# Filtrar días pasados
|
|
793
|
-
today =
|
|
834
|
+
today = get_local_datetime().date() # Usar la hora local
|
|
794
835
|
data["dies"] = [
|
|
795
836
|
dia for dia in data["dies"]
|
|
796
|
-
if
|
|
837
|
+
if convert_to_local_time(dia["data"], "Europe/Madrid").date() >= today
|
|
797
838
|
]
|
|
798
839
|
return data
|
|
799
840
|
except Exception as e:
|
|
@@ -806,9 +847,9 @@ class DailyForecastCoordinator(DataUpdateCoordinator):
|
|
|
806
847
|
if not self.data or "dies" not in self.data or not self.data["dies"]:
|
|
807
848
|
return None
|
|
808
849
|
|
|
809
|
-
today =
|
|
850
|
+
today = get_local_datetime().date() # Usar la hora local
|
|
810
851
|
for dia in self.data["dies"]:
|
|
811
|
-
forecast_date =
|
|
852
|
+
forecast_date = convert_to_local_time(dia["data"], "Europe/Madrid").date()
|
|
812
853
|
if forecast_date == today:
|
|
813
854
|
return dia
|
|
814
855
|
return None
|
|
@@ -820,7 +861,7 @@ class DailyForecastCoordinator(DataUpdateCoordinator):
|
|
|
820
861
|
condition = get_condition_from_code(int(condition_code))
|
|
821
862
|
|
|
822
863
|
forecast_data = {
|
|
823
|
-
"date":
|
|
864
|
+
"date": convert_to_local_time(dia["data"], "Europe/Madrid").date(),
|
|
824
865
|
"temperature_max": float(variables.get("tmax", {}).get("valor", 0.0)),
|
|
825
866
|
"temperature_min": float(variables.get("tmin", {}).get("valor", 0.0)),
|
|
826
867
|
"precipitation": float(variables.get("precipitacio", {}).get("valor", 0.0)),
|
|
@@ -900,7 +941,7 @@ class MeteocatConditionCoordinator(DataUpdateCoordinator):
|
|
|
900
941
|
def _get_condition_for_current_hour(self, raw_data):
|
|
901
942
|
"""Get condition data for the current hour."""
|
|
902
943
|
# Fecha y hora actual
|
|
903
|
-
current_datetime =
|
|
944
|
+
current_datetime = get_local_datetime() # Usar la zona horaria local
|
|
904
945
|
current_date = current_datetime.strftime("%Y-%m-%d")
|
|
905
946
|
current_hour = current_datetime.hour
|
|
906
947
|
|
|
@@ -908,7 +949,7 @@ class MeteocatConditionCoordinator(DataUpdateCoordinator):
|
|
|
908
949
|
for day in raw_data.get("dies", []):
|
|
909
950
|
if day["data"].startswith(current_date):
|
|
910
951
|
for value in day["variables"]["estatCel"]["valors"]:
|
|
911
|
-
data_hour =
|
|
952
|
+
data_hour = convert_to_local_time(value["data"])
|
|
912
953
|
if data_hour.hour == current_hour:
|
|
913
954
|
codi_estatcel = value["valor"]
|
|
914
955
|
condition = get_condition_from_statcel(
|
|
@@ -979,9 +1020,9 @@ class MeteocatTempForecastCoordinator(DataUpdateCoordinator):
|
|
|
979
1020
|
if not data or "dies" not in data or not data["dies"]:
|
|
980
1021
|
return False
|
|
981
1022
|
|
|
982
|
-
today =
|
|
1023
|
+
today = get_local_datetime().date()
|
|
983
1024
|
for dia in data["dies"]:
|
|
984
|
-
forecast_date =
|
|
1025
|
+
forecast_date = convert_to_local_time(dia["data"]).date() # Convertir a hora local
|
|
985
1026
|
if forecast_date >= today:
|
|
986
1027
|
return True
|
|
987
1028
|
|
|
@@ -999,10 +1040,10 @@ class MeteocatTempForecastCoordinator(DataUpdateCoordinator):
|
|
|
999
1040
|
data = json.loads(content)
|
|
1000
1041
|
|
|
1001
1042
|
# Filtrar días pasados
|
|
1002
|
-
today =
|
|
1043
|
+
today = get_local_datetime().date() # Usar la hora local
|
|
1003
1044
|
data["dies"] = [
|
|
1004
1045
|
dia for dia in data["dies"]
|
|
1005
|
-
if
|
|
1046
|
+
if convert_to_local_time(dia["data"]).date() >= today # Convertir a hora local
|
|
1006
1047
|
]
|
|
1007
1048
|
|
|
1008
1049
|
# Usar datos de temperatura del día actual si están disponibles
|
|
@@ -1020,9 +1061,9 @@ class MeteocatTempForecastCoordinator(DataUpdateCoordinator):
|
|
|
1020
1061
|
if not data or "dies" not in data or not data["dies"]:
|
|
1021
1062
|
return None
|
|
1022
1063
|
|
|
1023
|
-
today =
|
|
1064
|
+
today = get_local_datetime().date() # Usar la hora local
|
|
1024
1065
|
for dia in data["dies"]:
|
|
1025
|
-
forecast_date =
|
|
1066
|
+
forecast_date = convert_to_local_time(dia["data"]).date() # Convertir a hora local
|
|
1026
1067
|
if forecast_date == today:
|
|
1027
1068
|
return dia
|
|
1028
1069
|
return None
|
|
@@ -1032,7 +1073,7 @@ class MeteocatTempForecastCoordinator(DataUpdateCoordinator):
|
|
|
1032
1073
|
variables = dia.get("variables", {})
|
|
1033
1074
|
|
|
1034
1075
|
temp_forecast_data = {
|
|
1035
|
-
"date":
|
|
1076
|
+
"date": convert_to_local_time(dia["data"]), # Fecha convertida a hora local
|
|
1036
1077
|
"max_temp_forecast": float(variables.get("tmax", {}).get("valor", 0.0)),
|
|
1037
1078
|
"min_temp_forecast": float(variables.get("tmin", {}).get("valor", 0.0)),
|
|
1038
1079
|
}
|
|
@@ -1,7 +1,8 @@
|
|
|
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
|
+
from zoneinfo import ZoneInfo
|
|
5
6
|
import logging
|
|
6
7
|
from homeassistant.helpers.entity import (
|
|
7
8
|
DeviceInfo,
|
|
@@ -63,6 +64,9 @@ from .const import (
|
|
|
63
64
|
MIN_TEMPERATURE_CODE,
|
|
64
65
|
FEELS_LIKE,
|
|
65
66
|
WIND_GUST_CODE,
|
|
67
|
+
DEFAULT_VALIDITY_DAYS,
|
|
68
|
+
DEFAULT_VALIDITY_HOURS,
|
|
69
|
+
DEFAULT_VALIDITY_MINUTES,
|
|
66
70
|
)
|
|
67
71
|
|
|
68
72
|
from .coordinator import (
|
|
@@ -324,6 +328,29 @@ async def async_setup_entry(hass, entry, async_add_entities: AddEntitiesCallback
|
|
|
324
328
|
if description.key == UVI_FILE_STATUS
|
|
325
329
|
)
|
|
326
330
|
|
|
331
|
+
# Cambiar UTC a la zona horaria local
|
|
332
|
+
def convert_to_local_time(utc_time: str, local_tz: str = "Europe/Madrid") -> datetime | None:
|
|
333
|
+
"""
|
|
334
|
+
Convierte una fecha/hora UTC en formato ISO 8601 a la zona horaria local especificada.
|
|
335
|
+
|
|
336
|
+
Args:
|
|
337
|
+
utc_time (str): Fecha/hora en formato ISO 8601 (ejemplo: '2025-01-02T12:00:00Z').
|
|
338
|
+
local_tz (str): Zona horaria local en formato IANA (por defecto, 'Europe/Madrid').
|
|
339
|
+
|
|
340
|
+
Returns:
|
|
341
|
+
datetime | None: Objeto datetime convertido a la zona horaria local, o None si hay un error.
|
|
342
|
+
"""
|
|
343
|
+
try:
|
|
344
|
+
# Convertir la cadena UTC a un objeto datetime
|
|
345
|
+
utc_dt = datetime.fromisoformat(utc_time.replace("Z", "+00:00"))
|
|
346
|
+
|
|
347
|
+
# Convertir a la zona horaria local usando ZoneInfo
|
|
348
|
+
local_dt = utc_dt.replace(tzinfo=ZoneInfo("UTC")).astimezone(ZoneInfo(local_tz))
|
|
349
|
+
|
|
350
|
+
return local_dt
|
|
351
|
+
except ValueError:
|
|
352
|
+
return None
|
|
353
|
+
|
|
327
354
|
class MeteocatStaticSensor(CoordinatorEntity[MeteocatStaticSensorCoordinator], SensorEntity):
|
|
328
355
|
"""Representation of a static Meteocat sensor."""
|
|
329
356
|
STATIC_KEYS = {TOWN_NAME, TOWN_ID, STATION_NAME, STATION_ID}
|
|
@@ -630,9 +657,13 @@ class MeteocatSensor(CoordinatorEntity[MeteocatSensorCoordinator], SensorEntity)
|
|
|
630
657
|
if raw_timestamp:
|
|
631
658
|
# Convertir el timestamp a un objeto datetime
|
|
632
659
|
try:
|
|
633
|
-
|
|
660
|
+
# Convertimos raw_timestamp a hora local
|
|
661
|
+
local_time = convert_to_local_time(raw_timestamp)
|
|
662
|
+
_LOGGER.debug("Hora UTC: %s convertida a hora local: %s", raw_timestamp, local_time)
|
|
663
|
+
return local_time
|
|
634
664
|
except ValueError:
|
|
635
665
|
# Manejo de errores si el formato no es válido
|
|
666
|
+
_LOGGER.error(f"Error al convertir el timestamp '{raw_timestamp}' a hora local.")
|
|
636
667
|
return None
|
|
637
668
|
|
|
638
669
|
# Nuevo sensor para la precipitación acumulada
|
|
@@ -793,9 +824,21 @@ class MeteocatHourlyForecastStatusSensor(CoordinatorEntity[MeteocatEntityCoordin
|
|
|
793
824
|
first_date = self._get_first_date()
|
|
794
825
|
if first_date:
|
|
795
826
|
today = datetime.now(timezone.utc).date()
|
|
827
|
+
current_time = datetime.now(timezone.utc).time()
|
|
796
828
|
days_difference = (today - first_date).days
|
|
797
|
-
_LOGGER.debug(
|
|
798
|
-
|
|
829
|
+
_LOGGER.debug(
|
|
830
|
+
f"Diferencia de días para predicciones horarias: {days_difference}."
|
|
831
|
+
f"Hora actual de validación: {current_time}."
|
|
832
|
+
f"Para la validación: "
|
|
833
|
+
f"número de días= {DEFAULT_VALIDITY_DAYS}, "
|
|
834
|
+
f"hora de contacto a la API >= {DEFAULT_VALIDITY_HOURS}, "
|
|
835
|
+
f"minutos de contacto a la API >= {DEFAULT_VALIDITY_MINUTES}."
|
|
836
|
+
)
|
|
837
|
+
|
|
838
|
+
# Validar fecha y hora según la lógica del coordinador
|
|
839
|
+
if days_difference > DEFAULT_VALIDITY_DAYS and current_time >= time(DEFAULT_VALIDITY_HOURS, DEFAULT_VALIDITY_MINUTES):
|
|
840
|
+
return "obsolete"
|
|
841
|
+
return "updated"
|
|
799
842
|
return "unknown"
|
|
800
843
|
|
|
801
844
|
@property
|
|
@@ -844,9 +887,21 @@ class MeteocatDailyForecastStatusSensor(CoordinatorEntity[MeteocatEntityCoordina
|
|
|
844
887
|
first_date = self._get_first_date()
|
|
845
888
|
if first_date:
|
|
846
889
|
today = datetime.now(timezone.utc).date()
|
|
890
|
+
current_time = datetime.now(timezone.utc).time()
|
|
847
891
|
days_difference = (today - first_date).days
|
|
848
|
-
_LOGGER.debug(
|
|
849
|
-
|
|
892
|
+
_LOGGER.debug(
|
|
893
|
+
f"Diferencia de días para predicciones diarias: {days_difference}."
|
|
894
|
+
f"Hora actual de validación: {current_time}."
|
|
895
|
+
f"Para la validación: "
|
|
896
|
+
f"número de días= {DEFAULT_VALIDITY_DAYS}, "
|
|
897
|
+
f"hora de contacto a la API >= {DEFAULT_VALIDITY_HOURS}, "
|
|
898
|
+
f"minutos de contacto a la API >= {DEFAULT_VALIDITY_MINUTES}."
|
|
899
|
+
)
|
|
900
|
+
|
|
901
|
+
# Validar fecha y hora según la lógica del coordinador
|
|
902
|
+
if days_difference > DEFAULT_VALIDITY_DAYS and current_time >= time(DEFAULT_VALIDITY_HOURS, DEFAULT_VALIDITY_MINUTES):
|
|
903
|
+
return "obsolete"
|
|
904
|
+
return "updated"
|
|
850
905
|
return "unknown"
|
|
851
906
|
|
|
852
907
|
@property
|
|
@@ -894,9 +949,21 @@ class MeteocatUviStatusSensor(CoordinatorEntity[MeteocatUviCoordinator], SensorE
|
|
|
894
949
|
first_date = self._get_first_date()
|
|
895
950
|
if first_date:
|
|
896
951
|
today = datetime.now(timezone.utc).date()
|
|
952
|
+
current_time = datetime.now(timezone.utc).time()
|
|
897
953
|
days_difference = (today - first_date).days
|
|
898
|
-
_LOGGER.debug(
|
|
899
|
-
|
|
954
|
+
_LOGGER.debug(
|
|
955
|
+
f"Diferencia de días para datos UVI: {days_difference}."
|
|
956
|
+
f"Hora actual de validación: {current_time}."
|
|
957
|
+
f"Para la validación: "
|
|
958
|
+
f"número de días= {DEFAULT_VALIDITY_DAYS}, "
|
|
959
|
+
f"hora de contacto a la API >= {DEFAULT_VALIDITY_HOURS}, "
|
|
960
|
+
f"minutos de contacto a la API >= {DEFAULT_VALIDITY_MINUTES}."
|
|
961
|
+
)
|
|
962
|
+
|
|
963
|
+
# Validar fecha y hora según la lógica del coordinador
|
|
964
|
+
if days_difference > DEFAULT_VALIDITY_DAYS and current_time >= time(DEFAULT_VALIDITY_HOURS, DEFAULT_VALIDITY_MINUTES):
|
|
965
|
+
return "obsolete"
|
|
966
|
+
return "updated"
|
|
900
967
|
return "unknown"
|
|
901
968
|
|
|
902
969
|
@property
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
# version.py
|
|
2
|
-
__version__ = "0.1.
|
|
2
|
+
__version__ = "0.1.47"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "meteocat",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.47",
|
|
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": {
|