maj via codex
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
"""Service de collecte des données de la station météo locale WeeWX."""
|
||||
import html
|
||||
import logging
|
||||
import re
|
||||
import unicodedata
|
||||
import xml.etree.ElementTree as ET
|
||||
from datetime import datetime, timedelta, timezone
|
||||
|
||||
@@ -17,13 +19,37 @@ def _safe_float(text: str | None) -> float | None:
|
||||
try:
|
||||
cleaned = text.strip().replace(",", ".")
|
||||
# Retirer unités courantes
|
||||
for unit in [" °C", " %", " hPa", " km/h", " W/m²", "°C", "%", "hPa"]:
|
||||
for unit in [
|
||||
" °C",
|
||||
" %", " %",
|
||||
" hPa", " mbar",
|
||||
" km/h", " m/s",
|
||||
" mm/h", " mm",
|
||||
" W/m²", " W/m2",
|
||||
"°C", "%", "hPa", "mbar",
|
||||
]:
|
||||
cleaned = cleaned.replace(unit, "")
|
||||
return float(cleaned.strip())
|
||||
except (ValueError, AttributeError):
|
||||
return None
|
||||
|
||||
|
||||
def _normalize(text: str) -> str:
|
||||
text = unicodedata.normalize("NFKD", text)
|
||||
text = "".join(ch for ch in text if not unicodedata.combining(ch))
|
||||
text = text.lower()
|
||||
return re.sub(r"\s+", " ", text).strip()
|
||||
|
||||
|
||||
def _to_kmh(value: float | None, unit: str | None) -> float | None:
|
||||
if value is None:
|
||||
return None
|
||||
u = (unit or "").strip().lower()
|
||||
if u == "m/s":
|
||||
return round(value * 3.6, 1)
|
||||
return round(value, 1)
|
||||
|
||||
|
||||
def _direction_to_abbr(deg: float | None) -> str | None:
|
||||
if deg is None:
|
||||
return None
|
||||
@@ -51,37 +77,51 @@ def fetch_current(base_url: str = STATION_URL) -> dict | None:
|
||||
if item is None:
|
||||
return None
|
||||
|
||||
desc = item.findtext("description") or ""
|
||||
desc = html.unescape(item.findtext("description") or "")
|
||||
|
||||
result: dict = {}
|
||||
segments = [seg.strip() for seg in desc.split(";") if seg.strip()]
|
||||
for seg in segments:
|
||||
if ":" not in seg:
|
||||
continue
|
||||
raw_key, raw_value = seg.split(":", 1)
|
||||
key = _normalize(raw_key)
|
||||
value = raw_value.strip()
|
||||
|
||||
patterns = {
|
||||
"temp_ext": r"(?:Outside|Ext(?:erieur)?)\s*Temp(?:erature)?\s*[:\s]+(-?\d+(?:[.,]\d+)?)",
|
||||
"temp_int": r"(?:Inside|Int(?:erieur)?)\s*Temp(?:erature)?\s*[:\s]+(-?\d+(?:[.,]\d+)?)",
|
||||
"humidite": r"(?:Outside\s*)?Hum(?:idity)?\s*[:\s]+(\d+(?:[.,]\d+)?)",
|
||||
"pression": r"(?:Bar(?:ometer)?|Pression)\s*[:\s]+(\d+(?:[.,]\d+)?)",
|
||||
"pluie_mm": r"(?:Rain(?:fall)?|Pluie)\s*[:\s]+(\d+(?:[.,]\d+)?)",
|
||||
"vent_kmh": r"(?:Wind\s*Speed|Vent)\s*[:\s]+(\d+(?:[.,]\d+)?)",
|
||||
"uv": r"UV\s*[:\s]+(\d+(?:[.,]\d+)?)",
|
||||
"solaire": r"(?:Solar\s*Radiation|Solaire)\s*[:\s]+(\d+(?:[.,]\d+)?)",
|
||||
}
|
||||
if "temperature exterieure" in key or "outside temperature" in key:
|
||||
result["temp_ext"] = _safe_float(value)
|
||||
continue
|
||||
if "temperature interieure" in key or "inside temperature" in key:
|
||||
result["temp_int"] = _safe_float(value)
|
||||
continue
|
||||
if "hygrometrie exterieure" in key or "outside humidity" in key:
|
||||
result["humidite"] = _safe_float(value)
|
||||
continue
|
||||
if "pression atmospherique" in key or "barometer" in key:
|
||||
result["pression"] = _safe_float(value)
|
||||
continue
|
||||
if "precipitations" in key and "taux" not in key and "rate" not in key:
|
||||
result["pluie_mm"] = _safe_float(value)
|
||||
continue
|
||||
if key in {"uv", "ultra-violet"} or "ultra violet" in key:
|
||||
result["uv"] = _safe_float(value)
|
||||
continue
|
||||
if "rayonnement solaire" in key or "solar radiation" in key:
|
||||
result["solaire"] = _safe_float(value)
|
||||
continue
|
||||
if key == "vent" or "wind" in key:
|
||||
speed_match = re.search(r"(-?\d+(?:[.,]\d+)?)\s*(m/s|km/h)?", value, re.IGNORECASE)
|
||||
speed_val = _safe_float(speed_match.group(1)) if speed_match else None
|
||||
speed_unit = speed_match.group(2) if speed_match else None
|
||||
result["vent_kmh"] = _to_kmh(speed_val, speed_unit)
|
||||
|
||||
for key, pattern in patterns.items():
|
||||
m = re.search(pattern, desc, re.IGNORECASE)
|
||||
result[key] = _safe_float(m.group(1)) if m else None
|
||||
deg_match = re.search(r"(\d{1,3}(?:[.,]\d+)?)\s*°", value)
|
||||
if deg_match:
|
||||
result["vent_dir"] = _direction_to_abbr(_safe_float(deg_match.group(1)))
|
||||
continue
|
||||
|
||||
vent_dir_m = re.search(
|
||||
r"(?:Wind\s*Dir(?:ection)?)\s*[:\s]+([NSEO]{1,2}|Nord|Sud|Est|Ouest|\d+)",
|
||||
desc, re.IGNORECASE,
|
||||
)
|
||||
if vent_dir_m:
|
||||
val = vent_dir_m.group(1).strip()
|
||||
if val.isdigit():
|
||||
result["vent_dir"] = _direction_to_abbr(float(val))
|
||||
else:
|
||||
result["vent_dir"] = val[:2].upper()
|
||||
else:
|
||||
result["vent_dir"] = None
|
||||
card_match = re.search(r"\b(N|NE|E|SE|S|SO|O|NO|NNE|ENE|ESE|SSE|SSO|OSO|ONO|NNO)\b", value, re.IGNORECASE)
|
||||
result["vent_dir"] = card_match.group(1).upper() if card_match else None
|
||||
|
||||
return result if any(v is not None for v in result.values()) else None
|
||||
|
||||
@@ -107,15 +147,28 @@ def fetch_yesterday_summary(base_url: str = STATION_URL) -> dict | None:
|
||||
|
||||
for line in r.text.splitlines():
|
||||
parts = line.split()
|
||||
if len(parts) >= 7 and parts[0].isdigit() and int(parts[0]) == day:
|
||||
# Format NOAA : jour tmax tmin tmoy precip ...
|
||||
if not parts or not parts[0].isdigit() or int(parts[0]) != day:
|
||||
continue
|
||||
|
||||
# Format WeeWX NOAA (fréquent) :
|
||||
# day mean max hh:mm min hh:mm HDD CDD rain wind_avg wind_max hh:mm dir
|
||||
if len(parts) >= 11 and ":" in parts[3] and ":" in parts[5]:
|
||||
return {
|
||||
"t_max": _safe_float(parts[1]),
|
||||
"t_min": _safe_float(parts[2]),
|
||||
"temp_ext": _safe_float(parts[3]),
|
||||
"pluie_mm": _safe_float(parts[5]),
|
||||
"vent_kmh": _safe_float(parts[6]) if len(parts) > 6 else None,
|
||||
"temp_ext": _safe_float(parts[1]),
|
||||
"t_max": _safe_float(parts[2]),
|
||||
"t_min": _safe_float(parts[4]),
|
||||
"pluie_mm": _safe_float(parts[8]),
|
||||
"vent_kmh": _to_kmh(_safe_float(parts[10]), "m/s"),
|
||||
}
|
||||
|
||||
# Fallback générique (anciens formats)
|
||||
return {
|
||||
"t_max": _safe_float(parts[1]) if len(parts) > 1 else None,
|
||||
"t_min": _safe_float(parts[2]) if len(parts) > 2 else None,
|
||||
"temp_ext": _safe_float(parts[3]) if len(parts) > 3 else None,
|
||||
"pluie_mm": _safe_float(parts[5]) if len(parts) > 5 else None,
|
||||
"vent_kmh": _safe_float(parts[6]) if len(parts) > 6 else None,
|
||||
}
|
||||
except Exception as e:
|
||||
logger.warning(f"Station fetch_yesterday_summary error: {e}")
|
||||
return None
|
||||
|
||||
Reference in New Issue
Block a user