meteocat 3.0.0 → 3.2.0
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/ISSUE_TEMPLATE/bug_report.md +8 -2
- package/.github/ISSUE_TEMPLATE/config.yml +7 -0
- package/.github/ISSUE_TEMPLATE/improvement.md +39 -0
- package/.github/ISSUE_TEMPLATE/new_function.md +41 -0
- package/.github/labels.yml +63 -0
- package/.github/workflows/autocloser.yaml +11 -9
- package/.github/workflows/close-on-label.yml +48 -0
- package/.github/workflows/force-sync-labels.yml +18 -0
- package/.github/workflows/sync-gitlab.yml +15 -4
- package/.github/workflows/sync-labels.yml +21 -0
- package/CHANGELOG.md +80 -11
- package/README.md +16 -4
- package/custom_components/meteocat/__init__.py +57 -42
- package/custom_components/meteocat/condition.py +6 -2
- package/custom_components/meteocat/config_flow.py +231 -4
- package/custom_components/meteocat/const.py +17 -2
- package/custom_components/meteocat/coordinator.py +1122 -101
- package/custom_components/meteocat/helpers.py +31 -36
- package/custom_components/meteocat/manifest.json +3 -2
- package/custom_components/meteocat/options_flow.py +71 -3
- package/custom_components/meteocat/sensor.py +660 -247
- package/custom_components/meteocat/strings.json +252 -15
- package/custom_components/meteocat/translations/ca.json +249 -13
- package/custom_components/meteocat/translations/en.json +252 -15
- package/custom_components/meteocat/translations/es.json +252 -15
- package/custom_components/meteocat/version.py +1 -1
- package/filetree.txt +12 -3
- package/hacs.json +1 -1
- package/images/daily_forecast_2_alerts.png +0 -0
- package/images/daily_forecast_no_alerts.png +0 -0
- package/images/diagnostic_sensors.png +0 -0
- package/images/dynamic_sensors.png +0 -0
- package/images/options.png +0 -0
- package/images/regenerate_assets.png +0 -0
- package/images/setup_options.png +0 -0
- package/images/system_options.png +0 -0
- package/package.json +1 -1
- package/pyproject.toml +1 -1
- package/scripts/update_version.sh +6 -0
- package/.github/workflows/close-duplicates.yml +0 -57
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from datetime import datetime
|
|
4
|
+
from typing import Any
|
|
4
5
|
from .const import CONDITION_MAPPING
|
|
5
6
|
from .helpers import is_night
|
|
6
7
|
import logging
|
|
@@ -8,7 +9,10 @@ import logging
|
|
|
8
9
|
_LOGGER = logging.getLogger(__name__)
|
|
9
10
|
|
|
10
11
|
def get_condition_from_statcel(
|
|
11
|
-
codi_estatcel
|
|
12
|
+
codi_estatcel: Any,
|
|
13
|
+
current_time: datetime,
|
|
14
|
+
location,
|
|
15
|
+
is_hourly: bool = True
|
|
12
16
|
) -> dict:
|
|
13
17
|
"""
|
|
14
18
|
Convierte el código 'estatCel' en condición de Home Assistant.
|
|
@@ -33,7 +37,7 @@ def get_condition_from_statcel(
|
|
|
33
37
|
codi_estatcel = [codi_estatcel]
|
|
34
38
|
|
|
35
39
|
# Determinar si es de noche
|
|
36
|
-
is_night_flag = is_night(current_time,
|
|
40
|
+
is_night_flag = is_night(current_time, location)
|
|
37
41
|
|
|
38
42
|
# Identificar la condición basada en el código
|
|
39
43
|
for condition, codes in CONDITION_MAPPING.items():
|
|
@@ -5,13 +5,25 @@ import json
|
|
|
5
5
|
import logging
|
|
6
6
|
from pathlib import Path
|
|
7
7
|
from typing import Any
|
|
8
|
-
from datetime import datetime, timezone
|
|
8
|
+
from datetime import date, datetime, timezone, timedelta
|
|
9
9
|
from zoneinfo import ZoneInfo
|
|
10
10
|
|
|
11
11
|
import voluptuous as vol
|
|
12
12
|
import aiofiles
|
|
13
13
|
import unicodedata
|
|
14
14
|
|
|
15
|
+
from solarmoonpy.location import Location, LocationInfo
|
|
16
|
+
from solarmoonpy.moon import (
|
|
17
|
+
moon_phase,
|
|
18
|
+
moon_day,
|
|
19
|
+
moon_rise_set,
|
|
20
|
+
illuminated_percentage,
|
|
21
|
+
moon_distance,
|
|
22
|
+
moon_angular_diameter,
|
|
23
|
+
lunation_number,
|
|
24
|
+
get_moon_phase_name,
|
|
25
|
+
get_lunation_duration
|
|
26
|
+
)
|
|
15
27
|
from homeassistant.config_entries import ConfigEntry, ConfigFlow, ConfigFlowResult
|
|
16
28
|
from homeassistant.core import callback
|
|
17
29
|
from homeassistant.exceptions import HomeAssistantError
|
|
@@ -39,8 +51,8 @@ from .const import (
|
|
|
39
51
|
LIMIT_XEMA,
|
|
40
52
|
LIMIT_PREDICCIO,
|
|
41
53
|
LIMIT_XDDE,
|
|
42
|
-
LIMIT_BASIC,
|
|
43
54
|
LIMIT_QUOTA,
|
|
55
|
+
LIMIT_BASIC,
|
|
44
56
|
)
|
|
45
57
|
|
|
46
58
|
from .options_flow import MeteocatOptionsFlowHandler
|
|
@@ -88,6 +100,8 @@ class MeteocatConfigFlow(ConfigFlow, domain=DOMAIN):
|
|
|
88
100
|
self.longitude: float | None = None
|
|
89
101
|
self.altitude: float | None = None
|
|
90
102
|
self.station_status: str | None = None
|
|
103
|
+
self.location: Location | None = None
|
|
104
|
+
self.timezone_str: str | None = None
|
|
91
105
|
|
|
92
106
|
async def fetch_and_save_quotes(self, api_key: str):
|
|
93
107
|
"""Obtiene las cuotas de la API de Meteocat y las guarda en quotes.json."""
|
|
@@ -172,7 +186,7 @@ class MeteocatConfigFlow(ConfigFlow, domain=DOMAIN):
|
|
|
172
186
|
"Archivo regional %s creado con plantilla inicial", alerts_region_file
|
|
173
187
|
)
|
|
174
188
|
|
|
175
|
-
|
|
189
|
+
# Archivo lightning regional
|
|
176
190
|
lightning_file = alerts_dir / f"lightning_{self.region_id}.json"
|
|
177
191
|
if not lightning_file.exists():
|
|
178
192
|
async with aiofiles.open(lightning_file, "w", encoding="utf-8") as file:
|
|
@@ -183,6 +197,216 @@ class MeteocatConfigFlow(ConfigFlow, domain=DOMAIN):
|
|
|
183
197
|
"Archivo lightning %s creado con plantilla inicial", lightning_file
|
|
184
198
|
)
|
|
185
199
|
|
|
200
|
+
async def create_sun_file(self):
|
|
201
|
+
"""Crea el archivo sun_{town_id}_data.json con eventos solares + posición inicial del sol."""
|
|
202
|
+
if not self.selected_municipi or self.latitude is None or self.longitude is None:
|
|
203
|
+
_LOGGER.warning("No se puede crear sun_{town_id}_data.json: faltan municipio o coordenadas")
|
|
204
|
+
return
|
|
205
|
+
|
|
206
|
+
town_id = self.selected_municipi["codi"]
|
|
207
|
+
files_dir = get_storage_dir(self.hass, "files")
|
|
208
|
+
sun_file = files_dir / f"sun_{town_id.lower()}_data.json"
|
|
209
|
+
|
|
210
|
+
if sun_file.exists():
|
|
211
|
+
_LOGGER.debug("El archivo %s ya existe, no se crea de nuevo.", sun_file)
|
|
212
|
+
return
|
|
213
|
+
|
|
214
|
+
try:
|
|
215
|
+
# ZONA HORARIA DEL HASS
|
|
216
|
+
self.timezone_str = self.hass.config.time_zone or "Europe/Madrid"
|
|
217
|
+
tz = ZoneInfo(self.timezone_str)
|
|
218
|
+
|
|
219
|
+
# CREAR UBICACIÓN
|
|
220
|
+
self.location = Location(LocationInfo(
|
|
221
|
+
name=self.selected_municipi.get("nom", "Municipio"),
|
|
222
|
+
region="Spain",
|
|
223
|
+
timezone=self.timezone_str,
|
|
224
|
+
latitude=self.latitude,
|
|
225
|
+
longitude=self.longitude,
|
|
226
|
+
elevation=self.altitude or 0.0,
|
|
227
|
+
))
|
|
228
|
+
|
|
229
|
+
now = datetime.now(tz)
|
|
230
|
+
today = now.date()
|
|
231
|
+
tomorrow = today + timedelta(days=1)
|
|
232
|
+
|
|
233
|
+
# EVENTOS HOY Y MAÑANA
|
|
234
|
+
events_today = self.location.sun_events(date=today, local=True)
|
|
235
|
+
events_tomorrow = self.location.sun_events(date=tomorrow, local=True)
|
|
236
|
+
|
|
237
|
+
# LÓGICA DE EVENTOS (igual que en el coordinador)
|
|
238
|
+
expected = {}
|
|
239
|
+
events_list = [
|
|
240
|
+
"dawn_astronomical", "dawn_nautical", "dawn_civil",
|
|
241
|
+
"sunrise", "noon", "sunset",
|
|
242
|
+
"dusk_civil", "dusk_nautical", "dusk_astronomical",
|
|
243
|
+
"midnight",
|
|
244
|
+
]
|
|
245
|
+
|
|
246
|
+
for event in events_list:
|
|
247
|
+
event_time = events_today.get(event)
|
|
248
|
+
if event_time and now >= event_time:
|
|
249
|
+
expected[event] = events_tomorrow.get(event)
|
|
250
|
+
else:
|
|
251
|
+
expected[event] = event_time
|
|
252
|
+
|
|
253
|
+
# daylight_duration según sunrise
|
|
254
|
+
sunrise = expected["sunrise"]
|
|
255
|
+
expected["daylight_duration"] = (
|
|
256
|
+
events_tomorrow["daylight_duration"]
|
|
257
|
+
if sunrise == events_tomorrow["sunrise"]
|
|
258
|
+
else events_today["daylight_duration"]
|
|
259
|
+
)
|
|
260
|
+
|
|
261
|
+
# POSICIÓN ACTUAL DEL SOL
|
|
262
|
+
sun_pos = self.location.sun_position(dt=now, local=True)
|
|
263
|
+
|
|
264
|
+
# CONSTRUIR DADES
|
|
265
|
+
dades_dict = {
|
|
266
|
+
"dawn_civil": expected["dawn_civil"].isoformat() if expected["dawn_civil"] else None,
|
|
267
|
+
"dawn_nautical": expected["dawn_nautical"].isoformat() if expected["dawn_nautical"] else None,
|
|
268
|
+
"dawn_astronomical": expected["dawn_astronomical"].isoformat() if expected["dawn_astronomical"] else None,
|
|
269
|
+
"sunrise": expected["sunrise"].isoformat() if expected["sunrise"] else None,
|
|
270
|
+
"noon": expected["noon"].isoformat() if expected["noon"] else None,
|
|
271
|
+
"sunset": expected["sunset"].isoformat() if expected["sunset"] else None,
|
|
272
|
+
"dusk_civil": expected["dusk_civil"].isoformat() if expected["dusk_civil"] else None,
|
|
273
|
+
"dusk_nautical": expected["dusk_nautical"].isoformat() if expected["dusk_nautical"] else None,
|
|
274
|
+
"dusk_astronomical": expected["dusk_astronomical"].isoformat() if expected["dusk_astronomical"] else None,
|
|
275
|
+
"midnight": expected["midnight"].isoformat() if expected["midnight"] else None,
|
|
276
|
+
"daylight_duration": expected["daylight_duration"],
|
|
277
|
+
|
|
278
|
+
# CAMPOS DE POSICIÓN SOLAR
|
|
279
|
+
"sun_elevation": round(sun_pos["elevation"], 2),
|
|
280
|
+
"sun_azimuth": round(sun_pos["azimuth"], 2),
|
|
281
|
+
"sun_horizon_position": sun_pos["horizon_position"],
|
|
282
|
+
"sun_rising": sun_pos["rising"],
|
|
283
|
+
"sun_position_updated": now.isoformat(),
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
# JSON FINAL
|
|
287
|
+
data_with_timestamp = {
|
|
288
|
+
"actualitzat": {"dataUpdate": now.isoformat()},
|
|
289
|
+
"dades": [dades_dict],
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
# GUARDAR
|
|
293
|
+
sun_file.parent.mkdir(parents=True, exist_ok=True)
|
|
294
|
+
async with aiofiles.open(sun_file, "w", encoding="utf-8") as file:
|
|
295
|
+
await file.write(json.dumps(data_with_timestamp, ensure_ascii=False, indent=4))
|
|
296
|
+
|
|
297
|
+
_LOGGER.info(
|
|
298
|
+
"Archivo sun_%s_data.json creado con eventos + posición solar inicial (elev=%.2f°, az=%.2f°)",
|
|
299
|
+
town_id, sun_pos["elevation"], sun_pos["azimuth"]
|
|
300
|
+
)
|
|
301
|
+
|
|
302
|
+
except Exception as ex:
|
|
303
|
+
_LOGGER.error("Error al crear sun_%s_data.json: %s", town_id, ex)
|
|
304
|
+
|
|
305
|
+
async def create_moon_file(self):
|
|
306
|
+
"""Crea el archivo moon_{town_id}_data.json con datos iniciales de la fase lunar, moonrise y moonset."""
|
|
307
|
+
if not self.selected_municipi or not self.latitude or not self.longitude:
|
|
308
|
+
_LOGGER.warning("No se puede crear moon_{town_id}_data.json: faltan municipio o coordenadas")
|
|
309
|
+
return
|
|
310
|
+
|
|
311
|
+
town_id = self.selected_municipi["codi"]
|
|
312
|
+
files_dir = get_storage_dir(self.hass, "files")
|
|
313
|
+
moon_file = files_dir / f"moon_{town_id}_data.json"
|
|
314
|
+
|
|
315
|
+
if not moon_file.exists():
|
|
316
|
+
try:
|
|
317
|
+
# Fecha actual en UTC
|
|
318
|
+
current_time = datetime.now(timezone.utc).astimezone(TIMEZONE)
|
|
319
|
+
today = current_time.date()
|
|
320
|
+
|
|
321
|
+
# Inicializar parámetros con valores por defecto
|
|
322
|
+
phase = None
|
|
323
|
+
moon_day_today = None
|
|
324
|
+
lunation = None
|
|
325
|
+
illuminated = None
|
|
326
|
+
distance = None
|
|
327
|
+
angular_diameter = None
|
|
328
|
+
moon_phase_name = None
|
|
329
|
+
lunation_duration = None
|
|
330
|
+
|
|
331
|
+
# Calcular parámetros con manejo de errores individual
|
|
332
|
+
try:
|
|
333
|
+
phase = round(moon_phase(today), 2)
|
|
334
|
+
except Exception as ex:
|
|
335
|
+
_LOGGER.error("Error al calcular moon_phase: %s", ex)
|
|
336
|
+
|
|
337
|
+
try:
|
|
338
|
+
moon_day_today = moon_day(today)
|
|
339
|
+
except Exception as ex:
|
|
340
|
+
_LOGGER.error("Error al calcular moon_day: %s", ex)
|
|
341
|
+
|
|
342
|
+
try:
|
|
343
|
+
lunation = lunation_number(today)
|
|
344
|
+
except Exception as ex:
|
|
345
|
+
_LOGGER.error("Error al calcular lunation_number: %s", ex)
|
|
346
|
+
|
|
347
|
+
try:
|
|
348
|
+
illuminated = round(illuminated_percentage(today), 2)
|
|
349
|
+
except Exception as ex:
|
|
350
|
+
_LOGGER.error("Error al calcular illuminated_percentage: %s", ex)
|
|
351
|
+
|
|
352
|
+
try:
|
|
353
|
+
distance = round(moon_distance(today), 0)
|
|
354
|
+
except Exception as ex:
|
|
355
|
+
_LOGGER.error("Error al calcular moon_distance: %s", ex)
|
|
356
|
+
|
|
357
|
+
try:
|
|
358
|
+
angular_diameter = round(moon_angular_diameter(today), 2)
|
|
359
|
+
except Exception as ex:
|
|
360
|
+
_LOGGER.error("Error al calcular moon_angular_diameter: %s", ex)
|
|
361
|
+
|
|
362
|
+
try:
|
|
363
|
+
moon_phase_name = get_moon_phase_name(today)
|
|
364
|
+
except Exception as ex:
|
|
365
|
+
_LOGGER.error("Error al calcular moon_phase_name: %s", ex)
|
|
366
|
+
|
|
367
|
+
try:
|
|
368
|
+
lunation_duration = get_lunation_duration(today)
|
|
369
|
+
except Exception as ex:
|
|
370
|
+
_LOGGER.error("Error al calcular lunation_duration: %s", ex)
|
|
371
|
+
|
|
372
|
+
# Moonrise y moonset aproximados (UTC)
|
|
373
|
+
try:
|
|
374
|
+
rise_utc, set_utc = moon_rise_set(self.latitude, self.longitude, today)
|
|
375
|
+
rise_local = rise_utc.astimezone(TIMEZONE).isoformat() if rise_utc else None
|
|
376
|
+
set_local = set_utc.astimezone(TIMEZONE).isoformat() if set_utc else None
|
|
377
|
+
except Exception as ex:
|
|
378
|
+
_LOGGER.error("Error al calcular moon_rise_set: %s", ex)
|
|
379
|
+
rise_local = None
|
|
380
|
+
set_local = None
|
|
381
|
+
|
|
382
|
+
# Formatear datos para guardar
|
|
383
|
+
moon_data_formatted = {
|
|
384
|
+
"actualitzat": {"dataUpdate": current_time.isoformat()},
|
|
385
|
+
"last_lunar_update_date": today.isoformat(),
|
|
386
|
+
"dades": [
|
|
387
|
+
{
|
|
388
|
+
"moon_day": moon_day_today,
|
|
389
|
+
"moon_phase": phase,
|
|
390
|
+
"moon_phase_name": moon_phase_name,
|
|
391
|
+
"illuminated_percentage": illuminated,
|
|
392
|
+
"moon_distance": distance,
|
|
393
|
+
"moon_angular_diameter": angular_diameter,
|
|
394
|
+
"lunation": lunation,
|
|
395
|
+
"lunation_duration": lunation_duration,
|
|
396
|
+
"moonrise": rise_local,
|
|
397
|
+
"moonset": set_local
|
|
398
|
+
}
|
|
399
|
+
]
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
# Guardar el archivo
|
|
403
|
+
async with aiofiles.open(moon_file, "w", encoding="utf-8") as file:
|
|
404
|
+
await file.write(json.dumps(moon_data_formatted, ensure_ascii=False, indent=4))
|
|
405
|
+
_LOGGER.info("Archivo moon_%s_data.json creado con datos iniciales", town_id)
|
|
406
|
+
|
|
407
|
+
except Exception as ex:
|
|
408
|
+
_LOGGER.error("Error general al crear moon_%s_data.json: %s", town_id, ex)
|
|
409
|
+
|
|
186
410
|
async def async_step_user(
|
|
187
411
|
self, user_input: dict[str, Any] | None = None
|
|
188
412
|
) -> ConfigFlowResult:
|
|
@@ -319,7 +543,10 @@ class MeteocatConfigFlow(ConfigFlow, domain=DOMAIN):
|
|
|
319
543
|
self.province_name = station_metadata.get("provincia", {}).get("nom", "")
|
|
320
544
|
self.station_status = station_metadata.get("estats", [{}])[0].get("codi", "")
|
|
321
545
|
|
|
546
|
+
# Crear archivos de alertas, sol y luna
|
|
322
547
|
await self.create_alerts_file()
|
|
548
|
+
await self.create_sun_file()
|
|
549
|
+
await self.create_moon_file()
|
|
323
550
|
return await self.async_step_set_api_limits()
|
|
324
551
|
except Exception as ex:
|
|
325
552
|
_LOGGER.error("Error al obtener los metadatos de la estación: %s", ex)
|
|
@@ -384,4 +611,4 @@ class MeteocatConfigFlow(ConfigFlow, domain=DOMAIN):
|
|
|
384
611
|
@callback
|
|
385
612
|
def async_get_options_flow(config_entry: ConfigEntry) -> MeteocatOptionsFlowHandler:
|
|
386
613
|
"""Devuelve el flujo de opciones para esta configuración."""
|
|
387
|
-
return MeteocatOptionsFlowHandler(config_entry)
|
|
614
|
+
return MeteocatOptionsFlowHandler(config_entry)
|
|
@@ -42,17 +42,29 @@ QUOTA_BASIC = "quota_basic"
|
|
|
42
42
|
QUOTA_XEMA = "quota_xema"
|
|
43
43
|
QUOTA_QUERIES = "quota_queries"
|
|
44
44
|
LIGHTNING_FILE_STATUS = "lightning_file_status"
|
|
45
|
+
SUN = "sun"
|
|
46
|
+
SUNRISE = "sunrise"
|
|
47
|
+
SUNSET = "sunset"
|
|
48
|
+
SUN_FILE_STATUS = "sun_file_status"
|
|
49
|
+
MOON_PHASE = "moon_phase"
|
|
50
|
+
MOON_FILE_STATUS = "moon_file_status"
|
|
51
|
+
MOONRISE = "moonrise"
|
|
52
|
+
MOONSET = "moonset"
|
|
45
53
|
|
|
46
54
|
from homeassistant.const import Platform
|
|
47
55
|
|
|
48
|
-
ATTRIBUTION = "Powered by Meteocatpy"
|
|
56
|
+
ATTRIBUTION = "Powered by Meteocatpy & Solarmoonpy"
|
|
49
57
|
PLATFORMS = [Platform.SENSOR, Platform.WEATHER]
|
|
50
58
|
DEFAULT_NAME = "METEOCAT"
|
|
51
59
|
|
|
52
60
|
# Tiempos para validación de API
|
|
53
61
|
DEFAULT_VALIDITY_DAYS = 1 # Número de días a partir de los cuales se considera que el archivo de información está obsoleto
|
|
54
|
-
DEFAULT_VALIDITY_HOURS =
|
|
62
|
+
DEFAULT_VALIDITY_HOURS = 6 # Hora a partir de la cual la API tiene la información actualizada de predicciones disponible para descarga
|
|
55
63
|
DEFAULT_VALIDITY_MINUTES = 0 # Minutos a partir de los cuales la API tiene la información actualizada de predicciones disponible para descarga
|
|
64
|
+
DEFAULT_UVI_LOW_VALIDITY_HOURS = 5 # Hora a partir de la cual la API tiene la información actualizada de datos UVI disponible para descarga con límite bajo de cuota
|
|
65
|
+
DEFAULT_UVI_LOW_VALIDITY_MINUTES = 0 # Minutos a partir de los cuales la API tiene la información actualizada de datos UVI disponible para descarga con límite bajo de cuota
|
|
66
|
+
DEFAULT_UVI_HIGH_VALIDITY_HOURS = 9 # Hora a partir de la cual la API tiene la información actualizada de datos UVI disponible para descarga con límite alto de cuota
|
|
67
|
+
DEFAULT_UVI_HIGH_VALIDITY_MINUTES = 0 # Minutos a partir de los cuales la API tiene la información actualizada de datos UVI disponible para descarga con límite alto de cuota
|
|
56
68
|
DEFAULT_ALERT_VALIDITY_TIME = 120 # Minutos a partir de los cuales las alertas están obsoletas y se se debe proceder a una nueva llamada a la API
|
|
57
69
|
DEFAULT_QUOTES_VALIDITY_TIME = 240 # Minutos a partir de los cuales los datos de cuotas están obsoletos y se se debe proceder a una nueva llamada a la API
|
|
58
70
|
DEFAULT_LIGHTNING_VALIDITY_TIME = 240 # Minutos a partir de los cuales los datos de rayos están obsoletos y se se debe proceder a una nueva llamada a la API
|
|
@@ -65,6 +77,9 @@ ALERT_VALIDITY_MULTIPLIER_200 = 6 # para 100 < limit_prediccio <= 200
|
|
|
65
77
|
ALERT_VALIDITY_MULTIPLIER_500 = 3 # para 200 < limit_prediccio <= 500
|
|
66
78
|
ALERT_VALIDITY_MULTIPLIER_DEFAULT = 1 # para limit_prediccio > 500
|
|
67
79
|
|
|
80
|
+
# CUOTA ALTA PARA FAVORECER ACTUALIZACIONES DIARIAS DE LAS PREDICCIONES
|
|
81
|
+
PREDICCIO_HIGH_QUOTA_LIMIT = 550
|
|
82
|
+
|
|
68
83
|
# Códigos de sensores de la API
|
|
69
84
|
WIND_SPEED = "wind_speed" # Velocidad del viento
|
|
70
85
|
WIND_DIRECTION = "wind_direction" # Dirección del viento
|