meteocat 0.1.42 → 0.1.44
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 +25 -0
- package/custom_components/meteocat/__init__.py +6 -3
- package/custom_components/meteocat/config_flow.py +53 -6
- package/custom_components/meteocat/const.py +12 -0
- package/custom_components/meteocat/coordinator.py +4 -13
- package/custom_components/meteocat/manifest.json +2 -2
- package/custom_components/meteocat/sensor.py +200 -2
- package/custom_components/meteocat/strings.json +36 -1
- package/custom_components/meteocat/translations/ca.json +36 -0
- package/custom_components/meteocat/translations/en.json +36 -0
- package/custom_components/meteocat/translations/es.json +36 -1
- 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,28 @@
|
|
|
1
|
+
## [0.1.44](https://github.com/figorr/meteocat/compare/v0.1.43...v0.1.44) (2024-12-30)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* 0.1.44 ([7a506ea](https://github.com/figorr/meteocat/commit/7a506eab7e1fa1c0975561d71520ec3e146019dd))
|
|
7
|
+
* add date attribute for status sensors ([5663be5](https://github.com/figorr/meteocat/commit/5663be50ba007debe349aa730a7e735f8d448a8b))
|
|
8
|
+
* add state translations for file status ([32bcd61](https://github.com/figorr/meteocat/commit/32bcd611cddf773046ee07043a8578a6cbc52f94))
|
|
9
|
+
* add translations for status sensors date attribute ([9504a58](https://github.com/figorr/meteocat/commit/9504a5809fbc83ed2bba0b507354260d6606b7c5))
|
|
10
|
+
|
|
11
|
+
## [0.1.43](https://github.com/figorr/meteocat/compare/v0.1.42...v0.1.43) (2024-12-30)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
### Bug Fixes
|
|
15
|
+
|
|
16
|
+
* 0.1.43 ([13185b2](https://github.com/figorr/meteocat/commit/13185b2276a5efb02b4fd18d07d30ab03ca7357d))
|
|
17
|
+
* add info station to config_flow ([6c8c652](https://github.com/figorr/meteocat/commit/6c8c652dc54788e2b471984726dc7dd3c0ba30d6))
|
|
18
|
+
* add new hourly, daily and uvi file status sensors ([42aa8b2](https://github.com/figorr/meteocat/commit/42aa8b2abbf6b333287c15d92e19b69d9630e62e))
|
|
19
|
+
* add new hourly, daily and uvi file status sensors translations ([5639489](https://github.com/figorr/meteocat/commit/563948999feb077a5effe9c07df9dda950469d73))
|
|
20
|
+
* add new hourly, daily and uvi sensor constants ([f7a4814](https://github.com/figorr/meteocat/commit/f7a481435abf34926b52312cc5302045c9357e69))
|
|
21
|
+
* fix entity and uvi coordinator ([ee3a557](https://github.com/figorr/meteocat/commit/ee3a5571e5e08737b3393a650509732b1cd4996a))
|
|
22
|
+
* ignore test read json date ([f5ce2ed](https://github.com/figorr/meteocat/commit/f5ce2edfa77d1ec33c4beff8c5c775499c0564fe))
|
|
23
|
+
* include entity and uvi coordinators ([4128d39](https://github.com/figorr/meteocat/commit/4128d3902bd67414eaee6d8d6da4628ecbce3493))
|
|
24
|
+
* set region, province and status to str ([31f58e7](https://github.com/figorr/meteocat/commit/31f58e7d701b2499e4f4d9f385238368703c75ff))
|
|
25
|
+
|
|
1
26
|
## [0.1.42](https://github.com/figorr/meteocat/compare/v0.1.41...v0.1.42) (2024-12-28)
|
|
2
27
|
|
|
3
28
|
|
|
@@ -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.44"
|
|
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."""
|
|
@@ -53,7 +53,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|
|
53
53
|
# Validar campos requeridos
|
|
54
54
|
required_fields = [
|
|
55
55
|
"api_key", "town_name", "town_id", "variable_name",
|
|
56
|
-
"variable_id", "station_name", "station_id"
|
|
56
|
+
"variable_id", "station_name", "station_id", "province_name",
|
|
57
|
+
"province_id", "region_name", "region_id"
|
|
57
58
|
]
|
|
58
59
|
missing_fields = [field for field in required_fields if field not in entry_data]
|
|
59
60
|
if missing_fields:
|
|
@@ -63,7 +64,9 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|
|
63
64
|
_LOGGER.debug(
|
|
64
65
|
f"Datos de configuración: Municipio '{entry_data['town_name']}' (ID: {entry_data['town_id']}), "
|
|
65
66
|
f"Variable '{entry_data['variable_name']}' (ID: {entry_data['variable_id']}), "
|
|
66
|
-
f"Estación '{entry_data['station_name']}' (ID: {entry_data['station_id']})
|
|
67
|
+
f"Estación '{entry_data['station_name']}' (ID: {entry_data['station_id']}), "
|
|
68
|
+
f"Provincia '{entry_data['province_name']}' (ID: {entry_data['province_id']}), "
|
|
69
|
+
f"Comarca '{entry_data['region_name']}' (ID: {entry_data['region_id']})."
|
|
67
70
|
)
|
|
68
71
|
|
|
69
72
|
# Inicializar coordinadores
|
|
@@ -23,7 +23,16 @@ from .const import (
|
|
|
23
23
|
VARIABLE_NAME,
|
|
24
24
|
VARIABLE_ID,
|
|
25
25
|
STATION_NAME,
|
|
26
|
-
STATION_ID
|
|
26
|
+
STATION_ID,
|
|
27
|
+
STATION_TYPE,
|
|
28
|
+
LATITUDE,
|
|
29
|
+
LONGITUDE,
|
|
30
|
+
ALTITUDE,
|
|
31
|
+
REGION_ID,
|
|
32
|
+
REGION_NAME,
|
|
33
|
+
PROVINCE_ID,
|
|
34
|
+
PROVINCE_NAME,
|
|
35
|
+
STATION_STATUS
|
|
27
36
|
)
|
|
28
37
|
|
|
29
38
|
from .options_flow import MeteocatOptionsFlowHandler
|
|
@@ -31,6 +40,8 @@ from meteocatpy.town import MeteocatTown
|
|
|
31
40
|
from meteocatpy.symbols import MeteocatSymbols
|
|
32
41
|
from meteocatpy.variables import MeteocatVariables
|
|
33
42
|
from meteocatpy.townstations import MeteocatTownStations
|
|
43
|
+
from meteocatpy.infostation import MeteocatInfoStation
|
|
44
|
+
|
|
34
45
|
from meteocatpy.exceptions import BadRequestError, ForbiddenError, TooManyRequestsError, InternalServerError, UnknownAPIError
|
|
35
46
|
|
|
36
47
|
_LOGGER = logging.getLogger(__name__)
|
|
@@ -103,10 +114,17 @@ class MeteocatConfigFlow(ConfigFlow, domain=DOMAIN):
|
|
|
103
114
|
errors = {}
|
|
104
115
|
|
|
105
116
|
# Crear directorio de activos (assets) si no existe
|
|
106
|
-
assets_dir =
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
117
|
+
assets_dir = os.path.join(
|
|
118
|
+
self.hass.config.path(),
|
|
119
|
+
"custom_components",
|
|
120
|
+
"meteocat",
|
|
121
|
+
"assets"
|
|
122
|
+
)
|
|
123
|
+
os.makedirs(assets_dir, exist_ok=True)
|
|
124
|
+
|
|
125
|
+
# Rutas para los archivos de símbolos y variables
|
|
126
|
+
symbols_file = os.path.join(assets_dir, "symbols.json")
|
|
127
|
+
variables_file = os.path.join(assets_dir, "variables.json")
|
|
110
128
|
|
|
111
129
|
try:
|
|
112
130
|
# Descargar y guardar los símbolos
|
|
@@ -171,6 +189,26 @@ class MeteocatConfigFlow(ConfigFlow, domain=DOMAIN):
|
|
|
171
189
|
self.station_id = selected_station["codi"]
|
|
172
190
|
self.station_name = selected_station["nom"]
|
|
173
191
|
|
|
192
|
+
# Obtener metadatos de la estación
|
|
193
|
+
infostation_client = MeteocatInfoStation(self.api_key)
|
|
194
|
+
try:
|
|
195
|
+
station_metadata = await infostation_client.get_infostation(self.station_id)
|
|
196
|
+
except Exception as ex:
|
|
197
|
+
_LOGGER.error("Error al obtener los metadatos de la estación: %s", ex)
|
|
198
|
+
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
|
+
|
|
174
212
|
return self.async_create_entry(
|
|
175
213
|
title=self.selected_municipi["nom"],
|
|
176
214
|
data={
|
|
@@ -180,7 +218,16 @@ class MeteocatConfigFlow(ConfigFlow, domain=DOMAIN):
|
|
|
180
218
|
VARIABLE_NAME: "Temperatura",
|
|
181
219
|
VARIABLE_ID: str(self.variable_id),
|
|
182
220
|
STATION_NAME: self.station_name,
|
|
183
|
-
STATION_ID: self.station_id
|
|
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),
|
|
184
231
|
},
|
|
185
232
|
)
|
|
186
233
|
else:
|
|
@@ -8,6 +8,18 @@ VARIABLE_NAME = "variable_name"
|
|
|
8
8
|
VARIABLE_ID = "variable_id"
|
|
9
9
|
STATION_NAME = "station_name"
|
|
10
10
|
STATION_ID = "station_id"
|
|
11
|
+
STATION_TYPE = "station_type"
|
|
12
|
+
LATITUDE = "latitude"
|
|
13
|
+
LONGITUDE = "longitude"
|
|
14
|
+
ALTITUDE = "altitude"
|
|
15
|
+
REGION_ID = "region_id"
|
|
16
|
+
REGION_NAME = "region_name"
|
|
17
|
+
PROVINCE_ID = "province_id"
|
|
18
|
+
PROVINCE_NAME = "province_name"
|
|
19
|
+
STATION_STATUS = "station_status"
|
|
20
|
+
HOURLY_FORECAST_FILE_STATUS = "hourly_forecast_file_status"
|
|
21
|
+
DAILY_FORECAST_FILE_STATUS = "daily_forecast_file_status"
|
|
22
|
+
UVI_FILE_STATUS = "uvi_file_status"
|
|
11
23
|
|
|
12
24
|
from homeassistant.const import Platform
|
|
13
25
|
|
|
@@ -275,22 +275,15 @@ class MeteocatUviCoordinator(DataUpdateCoordinator):
|
|
|
275
275
|
"""Comprueba si el archivo JSON contiene datos válidos para el día actual y devuelve los datos si son válidos."""
|
|
276
276
|
try:
|
|
277
277
|
if not os.path.exists(self.uvi_file):
|
|
278
|
+
_LOGGER.info("El archivo %s no existe. Se considerará inválido.", self.uvi_file)
|
|
278
279
|
return None
|
|
279
280
|
|
|
280
281
|
async with aiofiles.open(self.uvi_file, "r", encoding="utf-8") as file:
|
|
281
282
|
content = await file.read()
|
|
282
283
|
data = json.loads(content)
|
|
283
284
|
|
|
284
|
-
# Verificar que el formato sea correcto
|
|
285
|
-
if not isinstance(data, dict) or "uvi" not in data:
|
|
286
|
-
return None
|
|
287
|
-
|
|
288
|
-
uvi_data = data["uvi"]
|
|
289
|
-
if not isinstance(uvi_data, list) or not uvi_data:
|
|
290
|
-
return None
|
|
291
|
-
|
|
292
285
|
# Validar la fecha del primer elemento superior a 1 día
|
|
293
|
-
first_date = datetime.strptime(
|
|
286
|
+
first_date = datetime.strptime(data["uvi"][0].get("date"), "%Y-%m-%d").date()
|
|
294
287
|
today = datetime.now(timezone.utc).date()
|
|
295
288
|
|
|
296
289
|
# Log detallado
|
|
@@ -319,7 +312,7 @@ class MeteocatUviCoordinator(DataUpdateCoordinator):
|
|
|
319
312
|
try:
|
|
320
313
|
# Validar el archivo JSON existente
|
|
321
314
|
valid_data = await self.is_uvi_data_valid()
|
|
322
|
-
if valid_data
|
|
315
|
+
if valid_data:
|
|
323
316
|
_LOGGER.info("Los datos del índice UV están actualizados. No se realiza llamada a la API.")
|
|
324
317
|
return valid_data['uvi']
|
|
325
318
|
|
|
@@ -511,9 +504,7 @@ class MeteocatEntityCoordinator(DataUpdateCoordinator):
|
|
|
511
504
|
async with aiofiles.open(file_path, "r", encoding="utf-8") as f:
|
|
512
505
|
content = await f.read()
|
|
513
506
|
data = json.loads(content)
|
|
514
|
-
|
|
515
|
-
_LOGGER.warning("El archivo %s no contiene datos válidos.", file_path)
|
|
516
|
-
return None
|
|
507
|
+
|
|
517
508
|
# Obtener la fecha del primer día
|
|
518
509
|
first_date = datetime.fromisoformat(data["dies"][0]["data"].rstrip("Z")).date()
|
|
519
510
|
today = datetime.now(timezone.utc).date()
|
|
@@ -7,6 +7,6 @@
|
|
|
7
7
|
"iot_class": "cloud_polling",
|
|
8
8
|
"documentation": "https://gitlab.com/figorr/meteocat",
|
|
9
9
|
"loggers": ["meteocatpy"],
|
|
10
|
-
"requirements": ["meteocatpy==0.0.
|
|
11
|
-
"version": "0.1.
|
|
10
|
+
"requirements": ["meteocatpy==0.0.17", "packaging>=20.3", "wrapt>=1.14.0"],
|
|
11
|
+
"version": "0.1.44"
|
|
12
12
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from dataclasses import dataclass
|
|
4
|
-
from datetime import datetime
|
|
4
|
+
from datetime import datetime, timezone
|
|
5
5
|
import logging
|
|
6
6
|
from homeassistant.helpers.entity import (
|
|
7
7
|
DeviceInfo,
|
|
@@ -48,6 +48,9 @@ from .const import (
|
|
|
48
48
|
CONDITION,
|
|
49
49
|
MAX_TEMPERATURE_FORECAST,
|
|
50
50
|
MIN_TEMPERATURE_FORECAST,
|
|
51
|
+
HOURLY_FORECAST_FILE_STATUS,
|
|
52
|
+
DAILY_FORECAST_FILE_STATUS,
|
|
53
|
+
UVI_FILE_STATUS,
|
|
51
54
|
WIND_SPEED_CODE,
|
|
52
55
|
WIND_DIRECTION_CODE,
|
|
53
56
|
TEMPERATURE_CODE,
|
|
@@ -68,6 +71,8 @@ from .coordinator import (
|
|
|
68
71
|
MeteocatUviFileCoordinator,
|
|
69
72
|
MeteocatConditionCoordinator,
|
|
70
73
|
MeteocatTempForecastCoordinator,
|
|
74
|
+
MeteocatEntityCoordinator,
|
|
75
|
+
MeteocatUviCoordinator,
|
|
71
76
|
)
|
|
72
77
|
|
|
73
78
|
_LOGGER = logging.getLogger(__name__)
|
|
@@ -229,6 +234,24 @@ SENSOR_TYPES: tuple[MeteocatSensorEntityDescription, ...] = (
|
|
|
229
234
|
state_class=SensorStateClass.MEASUREMENT,
|
|
230
235
|
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
|
|
231
236
|
),
|
|
237
|
+
MeteocatSensorEntityDescription(
|
|
238
|
+
key=HOURLY_FORECAST_FILE_STATUS,
|
|
239
|
+
translation_key="hourly_forecast_file_status",
|
|
240
|
+
icon="mdi:clock",
|
|
241
|
+
entity_category=EntityCategory.DIAGNOSTIC,
|
|
242
|
+
),
|
|
243
|
+
MeteocatSensorEntityDescription(
|
|
244
|
+
key=DAILY_FORECAST_FILE_STATUS,
|
|
245
|
+
translation_key="daily_forecast_file_status",
|
|
246
|
+
icon="mdi:clock",
|
|
247
|
+
entity_category=EntityCategory.DIAGNOSTIC,
|
|
248
|
+
),
|
|
249
|
+
MeteocatSensorEntityDescription(
|
|
250
|
+
key=UVI_FILE_STATUS,
|
|
251
|
+
translation_key="uvi_file_status",
|
|
252
|
+
icon="mdi:clock",
|
|
253
|
+
entity_category=EntityCategory.DIAGNOSTIC,
|
|
254
|
+
)
|
|
232
255
|
)
|
|
233
256
|
|
|
234
257
|
@callback
|
|
@@ -242,12 +265,14 @@ async def async_setup_entry(hass, entry, async_add_entities: AddEntitiesCallback
|
|
|
242
265
|
static_sensor_coordinator = entry_data.get("static_sensor_coordinator")
|
|
243
266
|
condition_coordinator = entry_data.get("condition_coordinator")
|
|
244
267
|
temp_forecast_coordinator = entry_data.get("temp_forecast_coordinator")
|
|
268
|
+
entity_coordinator = entry_data.get("entity_coordinator")
|
|
269
|
+
uvi_coordinator = entry_data.get("uvi_coordinator")
|
|
245
270
|
|
|
246
271
|
# Sensores generales
|
|
247
272
|
async_add_entities(
|
|
248
273
|
MeteocatSensor(coordinator, description, entry_data)
|
|
249
274
|
for description in SENSOR_TYPES
|
|
250
|
-
if description.key not in {TOWN_NAME, TOWN_ID, STATION_NAME, STATION_ID, UV_INDEX, CONDITION, MAX_TEMPERATURE_FORECAST, MIN_TEMPERATURE_FORECAST} # Excluir estáticos y UVI
|
|
275
|
+
if description.key not in {TOWN_NAME, TOWN_ID, STATION_NAME, STATION_ID, UV_INDEX, CONDITION, MAX_TEMPERATURE_FORECAST, MIN_TEMPERATURE_FORECAST, HOURLY_FORECAST_FILE_STATUS, DAILY_FORECAST_FILE_STATUS, UVI_FILE_STATUS} # Excluir estáticos y UVI
|
|
251
276
|
)
|
|
252
277
|
|
|
253
278
|
# Sensores estáticos
|
|
@@ -278,6 +303,27 @@ async def async_setup_entry(hass, entry, async_add_entities: AddEntitiesCallback
|
|
|
278
303
|
if description.key in {MAX_TEMPERATURE_FORECAST, MIN_TEMPERATURE_FORECAST}
|
|
279
304
|
)
|
|
280
305
|
|
|
306
|
+
# Sensores de estado de los archivos de previsión horaria
|
|
307
|
+
async_add_entities(
|
|
308
|
+
MeteocatHourlyForecastStatusSensor(entity_coordinator, description, entry_data)
|
|
309
|
+
for description in SENSOR_TYPES
|
|
310
|
+
if description.key == HOURLY_FORECAST_FILE_STATUS
|
|
311
|
+
)
|
|
312
|
+
|
|
313
|
+
# Sensores de estado de los archivos de previsión diaria
|
|
314
|
+
async_add_entities(
|
|
315
|
+
MeteocatDailyForecastStatusSensor(entity_coordinator, description, entry_data)
|
|
316
|
+
for description in SENSOR_TYPES
|
|
317
|
+
if description.key == DAILY_FORECAST_FILE_STATUS
|
|
318
|
+
)
|
|
319
|
+
|
|
320
|
+
# Sensores de estado de los archivos de uvi
|
|
321
|
+
async_add_entities(
|
|
322
|
+
MeteocatUviStatusSensor(uvi_coordinator, description, entry_data)
|
|
323
|
+
for description in SENSOR_TYPES
|
|
324
|
+
if description.key == UVI_FILE_STATUS
|
|
325
|
+
)
|
|
326
|
+
|
|
281
327
|
class MeteocatStaticSensor(CoordinatorEntity[MeteocatStaticSensorCoordinator], SensorEntity):
|
|
282
328
|
"""Representation of a static Meteocat sensor."""
|
|
283
329
|
STATIC_KEYS = {TOWN_NAME, TOWN_ID, STATION_NAME, STATION_ID}
|
|
@@ -718,3 +764,155 @@ class MeteocatTempForecast(CoordinatorEntity[MeteocatTempForecastCoordinator], S
|
|
|
718
764
|
manufacturer="Meteocat",
|
|
719
765
|
model="Meteocat API",
|
|
720
766
|
)
|
|
767
|
+
|
|
768
|
+
class MeteocatHourlyForecastStatusSensor(CoordinatorEntity[MeteocatEntityCoordinator], SensorEntity):
|
|
769
|
+
|
|
770
|
+
_attr_has_entity_name = True # Activa el uso de nombres basados en el dispositivo
|
|
771
|
+
|
|
772
|
+
def __init__(self, entity_coordinator, description, entry_data):
|
|
773
|
+
super().__init__(entity_coordinator)
|
|
774
|
+
self.entity_description = description
|
|
775
|
+
self._town_name = entry_data["town_name"]
|
|
776
|
+
self._town_id = entry_data["town_id"]
|
|
777
|
+
self._station_id = entry_data["station_id"]
|
|
778
|
+
|
|
779
|
+
# Unique ID for the entity
|
|
780
|
+
self._attr_unique_id = f"sensor.{DOMAIN}_{self._station_id}_hourly_status"
|
|
781
|
+
|
|
782
|
+
# Assign entity_category if defined in the description
|
|
783
|
+
self._attr_entity_category = getattr(description, "entity_category", None)
|
|
784
|
+
|
|
785
|
+
def _get_first_date(self):
|
|
786
|
+
hourly_data = self.coordinator.data.get("hourly")
|
|
787
|
+
if hourly_data and "dies" in hourly_data:
|
|
788
|
+
return datetime.fromisoformat(hourly_data["dies"][0]["data"].rstrip("Z")).date()
|
|
789
|
+
return None
|
|
790
|
+
|
|
791
|
+
@property
|
|
792
|
+
def native_value(self):
|
|
793
|
+
first_date = self._get_first_date()
|
|
794
|
+
if first_date:
|
|
795
|
+
today = datetime.now(timezone.utc).date()
|
|
796
|
+
days_difference = (today - first_date).days
|
|
797
|
+
_LOGGER.debug(f"Diferencia de días para predicciones horarias: {days_difference}")
|
|
798
|
+
return "updated" if days_difference <= 1 else "obsolete"
|
|
799
|
+
return "unknown"
|
|
800
|
+
|
|
801
|
+
@property
|
|
802
|
+
def extra_state_attributes(self):
|
|
803
|
+
attributes = super().extra_state_attributes or {}
|
|
804
|
+
first_date = self._get_first_date()
|
|
805
|
+
if first_date:
|
|
806
|
+
attributes["update_date"] = first_date.isoformat()
|
|
807
|
+
return attributes
|
|
808
|
+
|
|
809
|
+
@property
|
|
810
|
+
def device_info(self) -> DeviceInfo:
|
|
811
|
+
"""Return the device info."""
|
|
812
|
+
return DeviceInfo(
|
|
813
|
+
identifiers={(DOMAIN, self._town_id)},
|
|
814
|
+
name="Meteocat " + self._station_id + " " + self._town_name,
|
|
815
|
+
manufacturer="Meteocat",
|
|
816
|
+
model="Meteocat API",
|
|
817
|
+
)
|
|
818
|
+
|
|
819
|
+
class MeteocatDailyForecastStatusSensor(CoordinatorEntity[MeteocatEntityCoordinator], SensorEntity):
|
|
820
|
+
|
|
821
|
+
_attr_has_entity_name = True # Activa el uso de nombres basados en el dispositivo
|
|
822
|
+
|
|
823
|
+
def __init__(self, entity_coordinator, description, entry_data):
|
|
824
|
+
super().__init__(entity_coordinator)
|
|
825
|
+
self.entity_description = description
|
|
826
|
+
self._town_name = entry_data["town_name"]
|
|
827
|
+
self._town_id = entry_data["town_id"]
|
|
828
|
+
self._station_id = entry_data["station_id"]
|
|
829
|
+
|
|
830
|
+
# Unique ID for the entity
|
|
831
|
+
self._attr_unique_id = f"sensor.{DOMAIN}_{self._station_id}_daily_status"
|
|
832
|
+
|
|
833
|
+
# Assign entity_category if defined in the description
|
|
834
|
+
self._attr_entity_category = getattr(description, "entity_category", None)
|
|
835
|
+
|
|
836
|
+
def _get_first_date(self):
|
|
837
|
+
daily_data = self.coordinator.data.get("daily")
|
|
838
|
+
if daily_data and "dies" in daily_data:
|
|
839
|
+
return datetime.fromisoformat(daily_data["dies"][0]["data"].rstrip("Z")).date()
|
|
840
|
+
return None
|
|
841
|
+
|
|
842
|
+
@property
|
|
843
|
+
def native_value(self):
|
|
844
|
+
first_date = self._get_first_date()
|
|
845
|
+
if first_date:
|
|
846
|
+
today = datetime.now(timezone.utc).date()
|
|
847
|
+
days_difference = (today - first_date).days
|
|
848
|
+
_LOGGER.debug(f"Diferencia de días para predicciones diarias: {days_difference}")
|
|
849
|
+
return "updated" if days_difference <= 1 else "obsolete"
|
|
850
|
+
return "unknown"
|
|
851
|
+
|
|
852
|
+
@property
|
|
853
|
+
def extra_state_attributes(self):
|
|
854
|
+
attributes = super().extra_state_attributes or {}
|
|
855
|
+
first_date = self._get_first_date()
|
|
856
|
+
if first_date:
|
|
857
|
+
attributes["update_date"] = first_date.isoformat()
|
|
858
|
+
return attributes
|
|
859
|
+
|
|
860
|
+
@property
|
|
861
|
+
def device_info(self) -> DeviceInfo:
|
|
862
|
+
"""Return the device info."""
|
|
863
|
+
return DeviceInfo(
|
|
864
|
+
identifiers={(DOMAIN, self._town_id)},
|
|
865
|
+
name="Meteocat " + self._station_id + " " + self._town_name,
|
|
866
|
+
manufacturer="Meteocat",
|
|
867
|
+
model="Meteocat API",
|
|
868
|
+
)
|
|
869
|
+
|
|
870
|
+
class MeteocatUviStatusSensor(CoordinatorEntity[MeteocatUviCoordinator], SensorEntity):
|
|
871
|
+
|
|
872
|
+
_attr_has_entity_name = True # Activa el uso de nombres basados en el dispositivo
|
|
873
|
+
|
|
874
|
+
def __init__(self, uvi_coordinator, description, entry_data):
|
|
875
|
+
super().__init__(uvi_coordinator)
|
|
876
|
+
self.entity_description = description
|
|
877
|
+
self._town_name = entry_data["town_name"]
|
|
878
|
+
self._town_id = entry_data["town_id"]
|
|
879
|
+
self._station_id = entry_data["station_id"]
|
|
880
|
+
|
|
881
|
+
# Unique ID for the entity
|
|
882
|
+
self._attr_unique_id = f"sensor.{DOMAIN}_{self._station_id}_uvi_status"
|
|
883
|
+
|
|
884
|
+
# Assign entity_category if defined in the description
|
|
885
|
+
self._attr_entity_category = getattr(description, "entity_category", None)
|
|
886
|
+
|
|
887
|
+
def _get_first_date(self):
|
|
888
|
+
if self.coordinator.data:
|
|
889
|
+
return datetime.strptime(self.coordinator.data[0].get("date"), "%Y-%m-%d").date()
|
|
890
|
+
return None
|
|
891
|
+
|
|
892
|
+
@property
|
|
893
|
+
def native_value(self):
|
|
894
|
+
first_date = self._get_first_date()
|
|
895
|
+
if first_date:
|
|
896
|
+
today = datetime.now(timezone.utc).date()
|
|
897
|
+
days_difference = (today - first_date).days
|
|
898
|
+
_LOGGER.debug(f"Diferencia de días para UVI: {days_difference}")
|
|
899
|
+
return "updated" if days_difference <= 1 else "obsolete"
|
|
900
|
+
return "unknown"
|
|
901
|
+
|
|
902
|
+
@property
|
|
903
|
+
def extra_state_attributes(self):
|
|
904
|
+
attributes = super().extra_state_attributes or {}
|
|
905
|
+
first_date = self._get_first_date()
|
|
906
|
+
if first_date:
|
|
907
|
+
attributes["update_date"] = first_date.isoformat()
|
|
908
|
+
return attributes
|
|
909
|
+
|
|
910
|
+
@property
|
|
911
|
+
def device_info(self) -> DeviceInfo:
|
|
912
|
+
"""Return the device info."""
|
|
913
|
+
return DeviceInfo(
|
|
914
|
+
identifiers={(DOMAIN, self._town_id)},
|
|
915
|
+
name="Meteocat " + self._station_id + " " + self._town_name,
|
|
916
|
+
manufacturer="Meteocat",
|
|
917
|
+
model="Meteocat API",
|
|
918
|
+
)
|
|
@@ -133,8 +133,43 @@
|
|
|
133
133
|
},
|
|
134
134
|
"min_temperature_forecast": {
|
|
135
135
|
"name": "Min Temperature Today"
|
|
136
|
+
},
|
|
137
|
+
"hourly_forecast_file_status": {
|
|
138
|
+
"name": "Hourly File",
|
|
139
|
+
"state": {
|
|
140
|
+
"updated": "Updated",
|
|
141
|
+
"obsolete": "Obsolete"
|
|
142
|
+
},
|
|
143
|
+
"state_attributes": {
|
|
144
|
+
"update_date": {
|
|
145
|
+
"name": "Date"
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
},
|
|
149
|
+
"daily_forecast_file_status": {
|
|
150
|
+
"name": "Daily File",
|
|
151
|
+
"state": {
|
|
152
|
+
"updated": "Updated",
|
|
153
|
+
"obsolete": "Obsolete"
|
|
154
|
+
},
|
|
155
|
+
"state_attributes": {
|
|
156
|
+
"update_date": {
|
|
157
|
+
"name": "Date"
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
},
|
|
161
|
+
"uvi_file_status": {
|
|
162
|
+
"name": "Uvi File",
|
|
163
|
+
"state": {
|
|
164
|
+
"updated": "Updated",
|
|
165
|
+
"obsolete": "Obsolete"
|
|
166
|
+
},
|
|
167
|
+
"state_attributes": {
|
|
168
|
+
"update_date": {
|
|
169
|
+
"name": "Date"
|
|
170
|
+
}
|
|
171
|
+
}
|
|
136
172
|
}
|
|
137
173
|
}
|
|
138
174
|
}
|
|
139
175
|
}
|
|
140
|
-
|
|
@@ -133,6 +133,42 @@
|
|
|
133
133
|
},
|
|
134
134
|
"min_temperature_forecast": {
|
|
135
135
|
"name": "Temperatura Min Avui"
|
|
136
|
+
},
|
|
137
|
+
"hourly_forecast_file_status": {
|
|
138
|
+
"name": "Arxiu Horari",
|
|
139
|
+
"state": {
|
|
140
|
+
"updated": "Actualitzat",
|
|
141
|
+
"obsolete": "Obsolet"
|
|
142
|
+
},
|
|
143
|
+
"state_attributes": {
|
|
144
|
+
"update_date": {
|
|
145
|
+
"name": "Data"
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
},
|
|
149
|
+
"daily_forecast_file_status": {
|
|
150
|
+
"name": "Arxiu Diari",
|
|
151
|
+
"state": {
|
|
152
|
+
"updated": "Actualitzat",
|
|
153
|
+
"obsolete": "Obsolet"
|
|
154
|
+
},
|
|
155
|
+
"state_attributes": {
|
|
156
|
+
"update_date": {
|
|
157
|
+
"name": "Data"
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
},
|
|
161
|
+
"uvi_file_status": {
|
|
162
|
+
"name": "Arxiu UVI",
|
|
163
|
+
"state": {
|
|
164
|
+
"updated": "Actualitzat",
|
|
165
|
+
"obsolete": "Obsolet"
|
|
166
|
+
},
|
|
167
|
+
"state_attributes": {
|
|
168
|
+
"update_date": {
|
|
169
|
+
"name": "Data"
|
|
170
|
+
}
|
|
171
|
+
}
|
|
136
172
|
}
|
|
137
173
|
}
|
|
138
174
|
}
|
|
@@ -133,6 +133,42 @@
|
|
|
133
133
|
},
|
|
134
134
|
"min_temperature_forecast": {
|
|
135
135
|
"name": "Min Temperature Today"
|
|
136
|
+
},
|
|
137
|
+
"hourly_forecast_file_status": {
|
|
138
|
+
"name": "Hourly File",
|
|
139
|
+
"state": {
|
|
140
|
+
"updated": "Updated",
|
|
141
|
+
"obsolete": "Obsolete"
|
|
142
|
+
},
|
|
143
|
+
"state_attributes": {
|
|
144
|
+
"update_date": {
|
|
145
|
+
"name": "Date"
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
},
|
|
149
|
+
"daily_forecast_file_status": {
|
|
150
|
+
"name": "Daily File",
|
|
151
|
+
"state": {
|
|
152
|
+
"updated": "Updated",
|
|
153
|
+
"obsolete": "Obsolete"
|
|
154
|
+
},
|
|
155
|
+
"state_attributes": {
|
|
156
|
+
"update_date": {
|
|
157
|
+
"name": "Date"
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
},
|
|
161
|
+
"uvi_file_status": {
|
|
162
|
+
"name": "Uvi File",
|
|
163
|
+
"state": {
|
|
164
|
+
"updated": "Updated",
|
|
165
|
+
"obsolete": "Obsolete"
|
|
166
|
+
},
|
|
167
|
+
"state_attributes": {
|
|
168
|
+
"update_date": {
|
|
169
|
+
"name": "Date"
|
|
170
|
+
}
|
|
171
|
+
}
|
|
136
172
|
}
|
|
137
173
|
}
|
|
138
174
|
}
|
|
@@ -133,8 +133,43 @@
|
|
|
133
133
|
},
|
|
134
134
|
"min_temperature_forecast": {
|
|
135
135
|
"name": "Temperatura Min Hoy"
|
|
136
|
+
},
|
|
137
|
+
"hourly_forecast_file_status": {
|
|
138
|
+
"name": "Archivo Horario",
|
|
139
|
+
"state": {
|
|
140
|
+
"updated": "Actualizado",
|
|
141
|
+
"obsolete": "Obsoleto"
|
|
142
|
+
},
|
|
143
|
+
"state_attributes": {
|
|
144
|
+
"update_date": {
|
|
145
|
+
"name": "Fecha"
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
},
|
|
149
|
+
"daily_forecast_file_status": {
|
|
150
|
+
"name": "Archivo Diario",
|
|
151
|
+
"state": {
|
|
152
|
+
"updated": "Actualizado",
|
|
153
|
+
"obsolete": "Obsoleto"
|
|
154
|
+
},
|
|
155
|
+
"state_attributes": {
|
|
156
|
+
"update_date": {
|
|
157
|
+
"name": "Fecha"
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
},
|
|
161
|
+
"uvi_file_status": {
|
|
162
|
+
"name": "Archivo UVI",
|
|
163
|
+
"state": {
|
|
164
|
+
"updated": "Actualizado",
|
|
165
|
+
"obsolete": "Obsoleto"
|
|
166
|
+
},
|
|
167
|
+
"state_attributes": {
|
|
168
|
+
"update_date": {
|
|
169
|
+
"name": "Fecha"
|
|
170
|
+
}
|
|
171
|
+
}
|
|
136
172
|
}
|
|
137
173
|
}
|
|
138
174
|
}
|
|
139
175
|
}
|
|
140
|
-
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
# version.py
|
|
2
|
-
__version__ = "0.1.
|
|
2
|
+
__version__ = "0.1.44"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "meteocat",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.44",
|
|
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": {
|