#!/usr/bin/env python3 import os import sys import json import gi import requests from datetime import datetime, timezone gi.require_version('Geoclue', '2.0') from gi.repository import Geoclue # ─────────── ICONOS DEL CLIMA ─────────── # weather_icons = { "01d": "", "01n": "", "02d": "", "02n": "", "03d": "", "03n": "", "04d": "", "04n": "", "09d": "", "09n": "", "10d": "", "10n": "", "11d": "", "11n": "", "13d": "", "13n": "", "50d": "", "50n": "", "default": "" } # ─────────── API KEYS ─────────── # try: with open('/home/teraflops/apikeys/openweathermaps', 'r') as f: api_key = f.read().strip() with open('/home/teraflops/apikeys/google_geoip', 'r') as f: google_key = f.read().strip() except Exception: print(json.dumps({"text": "Error al leer las API keys"})) sys.exit(1) # ─────────── OBTENER UBICACIÓN ─────────── # def get_location_from_geoclue(): try: clue = Geoclue.Simple.new_sync("weather-script", Geoclue.AccuracyLevel.EXACT, None) location = clue.get_location() lat = location.get_property('latitude') lon = location.get_property('longitude') desc = location.get_property('description') acc = location.get_property('accuracy') print(f"🛰️ GeoClue: {lat}, {lon} — Precisión: {acc:.1f} m — Fuente: {desc}") return lat, lon, desc except Exception as e: print("GeoClue (GI): error al obtener ubicación:", e) return None def get_location_from_google(): try: url = f"https://www.googleapis.com/geolocation/v1/geolocate?key={google_key}" response = requests.post(url, json={}) response.raise_for_status() data = response.json() lat = data['location']['lat'] lon = data['location']['lng'] accuracy = data.get('accuracy', 0) print(f"🛰️ GoogleGeo: {lat}, {lon} — Precisión: {accuracy:.0f} m") return lat, lon, f"GoogleGeo ({accuracy:.0f} m)" except Exception as e: print("Google Geolocation API: fallo:", e) return None def reverse_geocode(lat, lon): try: url = "https://nominatim.openstreetmap.org/reverse" params = { "lat": lat, "lon": lon, "format": "json", "zoom": 10, "addressdetails": 1, } headers = {"User-Agent": "weather-waybar-script"} response = requests.get(url, params=params, headers=headers) response.raise_for_status() data = response.json() address = data.get("address", {}) ciudad = address.get("city") or address.get("town") or address.get("village") or "Desconocido" pais = address.get("country", "Desconocido") return ciudad, pais except Exception as e: print("Reverse geocode falló:", e) return "Desconocido", "Desconocido" # ─────────── ALERTA LLUVIA SOLO EN TOOLTIP ─────────── # def check_rain_alert(forecast_data, threshold=60, max_horas=6): ahora = datetime.utcnow() mensaje_alerta = "" for block in forecast_data['list']: bloque_hora = datetime.strptime(block['dt_txt'], "%Y-%m-%d %H:%M:%S") if bloque_hora <= ahora: continue if (bloque_hora - ahora).total_seconds() > max_horas * 3600: continue pop = block.get('pop', 0) * 100 if pop >= threshold: hora_local = bloque_hora.replace(tzinfo=timezone.utc).astimezone().strftime("%H:%M") mensaje_alerta = f"⚠️ Posible lluvia a las {hora_local} ({int(pop)}%)" break return mensaje_alerta # ─────────── PRONÓSTICO EXTENDIDO ─────────── # def get_daily_forecast(forecast_data): from collections import defaultdict dias = defaultdict(list) for entry in forecast_data['list']: fecha = entry['dt_txt'].split(' ')[0] dias[fecha].append(entry) resumen = [] for i, (dia, bloques) in enumerate(dias.items()): if i >= 3: break temps = [b['main']['temp'] for b in bloques] pops = [b.get('pop', 0) * 100 for b in bloques] estado = bloques[0]['weather'][0]['description'].capitalize() resumen.append(f"📅 {dia}: {estado}, {round(min(temps))}–{round(max(temps))}°C, ☔ {round(max(pops))}%") return "\n".join(resumen) # ─────────── UBICACIÓN ─────────── # usar_ubicacion_manual = False # Cambia a True si quieres forzar ubicación if usar_ubicacion_manual: latitud, longitud = 40.4168, -3.7038 # Ejemplo: Madrid region = "Manual" print(f"🧪 Usando ubicación manual: {latitud}, {longitud}") else: loc = get_location_from_geoclue() or get_location_from_google() if not loc: print(json.dumps({"text": "Error al obtener la ubicación"})) sys.exit(1) latitud, longitud, region = loc ciudad, pais = reverse_geocode(latitud, longitud) # ─────────── API WEATHER ─────────── # params = { "lat": latitud, "lon": longitud, "appid": api_key, "units": "metric", "lang": "es", } weather_url = "https://api.openweathermap.org/data/2.5/weather" response = requests.get(weather_url, params=params) if response.status_code != 200: print(json.dumps({"text": "Error al obtener datos del clima"})) sys.exit(1) data = response.json() # ─────────── DATOS CLIMA ─────────── # temp = f"{round(data['main']['temp'])}°C" status = data['weather'][0]['description'].capitalize() status_code = data['weather'][0]['icon'] icon = weather_icons.get(status_code, weather_icons["default"]) temp_feel = f"{round(data['main']['feels_like'])}°C" temp_feel_text = f"Sensación de {temp_feel}" temp_min = f"{round(data['main']['temp_min'])}°C" temp_max = f"{round(data['main']['temp_max'])}°C" temp_min_max = f" {temp_min}\t\t {temp_max}" wind_speed = f"{round(data['wind']['speed'] * 3.6)} km/h" wind_text = f" {wind_speed}" humidity = f"{data['main']['humidity']}%" humidity_text = f" {humidity}" visibility = data.get('visibility', 0) visibility_km = f"{visibility / 1000} km" visibility_text = f" {visibility_km}" # ─────────── CALIDAD DEL AIRE ─────────── # aqi_url = "https://api.openweathermap.org/data/2.5/air_pollution" aqi_response = requests.get(aqi_url, params=params) if aqi_response.status_code == 200: aqi = aqi_response.json()['list'][0]['main']['aqi'] aqi_levels = {1: "Bueno", 2: "Moderado", 3: "Perjudicial", 4: "Malo", 5: "Muy malo"} air_quality_index = aqi_levels.get(aqi, "N/A") else: air_quality_index = "N/A" # ─────────── PRONÓSTICO ─────────── # forecast_url = "https://api.openweathermap.org/data/2.5/forecast" forecast_response = requests.get(forecast_url, params=params) if forecast_response.status_code == 200: forecast_data = forecast_response.json() precipitation = forecast_data['list'][0].get('pop', 0) * 100 prediction = f"\n\n  {precipitation:.0f}% probabilidad de lluvia" alerta_lluvia = check_rain_alert(forecast_data) pronostico_dias = get_daily_forecast(forecast_data) else: prediction = "" alerta_lluvia = "" pronostico_dias = "" # ─────────── TOOLTIP ─────────── # time_of_day = "Día" if status_code.endswith('d') else "Noche" ubicacion = f"{ciudad}, {region}, {pais} ({latitud:.2f}, {longitud:.2f})" tooltip_text = "" if alerta_lluvia: tooltip_text += f"{alerta_lluvia}\n\n" tooltip_text += str.format( "{}\n{}\n{}\n{}\n\n{}\n{}\n{}{}\n\n{}", f'{temp}', f"{icon}", f"{status} ({time_of_day})", f"{temp_feel_text}", f"{temp_min_max}", f"{wind_text}\t{humidity_text}", f"{visibility_text}\tICA {air_quality_index}", f"{prediction}", f"Ubicación: {ubicacion}" ) if pronostico_dias: tooltip_text += f"\n\nPróximos días:\n{pronostico_dias}" # ─────────── OUTPUT PARA WAYBAR ─────────── # out_data = { "text": f"{icon} {temp}", "alt": f"{status} ({time_of_day})", "tooltip": tooltip_text, "class": status_code, } print(json.dumps(out_data))