Skill: didelco/pvpc-spain (version 1.0.0)

--- name: pvpc-spain description: Consulta y optimiza precios de electricidad PVPC en España (tarifa 2.0TD para usuarios domésticos). Usa cuando necesites (1) precio actual con contexto (alto/medio/bajo vs día), (2) identificar periodos valle/llano/punta según horario, (3) encontrar las horas más baratas del día, (4) optimizar cuándo usar electrodomésticos (lavadora, lavavajillas, secadora, etc.) para minimizar coste eléctrico. --- # PVPC España Skill para consultar precios PVPC (Precio Voluntario Pequeño Consumidor) en España y optimizar el consumo eléctrico. Todos los datos se obtienen de la API pública de ESIOS (Red Eléctrica de España) para la tarifa 2.0TD. ## Consultas disponibles ### 1. Precio actual con contexto Muestra el precio actual clasificado como ALTO/MEDIO/BAJO según percentiles del día. ```bash # Precio actual completo python scripts/get_pvpc.py --now # Clasificación detallada python scripts/precio_referencia.py --now ``` **Respuesta incluye:** - Precio actual (€/kWh) - Mínimo y máximo del día - Clasificación: BAJO (<percentil 30), MEDIO (30-70), ALTO (>70) - Desviación respecto a la media del día ### 2. Periodos tarifarios (valle/llano/punta) Identifica el periodo actual según tarifa 2.0TD, ajustado por día de la semana. ```bash # Periodo actual python scripts/tarifa_periodos.py --now # Ver todos los periodos python scripts/tarifa_periodos.py --all ``` **Periodos 2.0TD:** - **VALLE** 🌙: 00:00-08:00 (todos los días) + sábados/domingos completos - **LLANO** ⚡: 08:00-10:00, 14:00-18:00, 22:00-00:00 (lun-vie) - **PUNTA** 🔴: 10:00-14:00, 18:00-22:00 (lun-vie) **Nota:** Los periodos son iguales en horario de verano e invierno para 2.0TD. ### 3. Horas más baratas del día Encuentra rangos de horas con precios por debajo del percentil 30 del día. ```bash # Rangos baratos (por defecto percentil 30) python scripts/find_cheap_ranges.py # Ajustar percentil python scripts/find_cheap_ranges.py --percentile 40 ``` **Respuesta incluye:** - Rangos de 2+ horas consecutivas con precios bajos - Precio mínimo/máximo/medio de cada rango - Ahorro porcentual vs media del día - Ordenados por duración (rangos más largos primero) ### 4. Optimizar electrodomésticos Encuentra la ventana de N horas consecutivas con menor coste total. ```bash # Lavadora (2 horas por defecto) python scripts/optimize_appliance.py --duration 2 --name lavadora # Lavavajillas (3 horas) python scripts/optimize_appliance.py --duration 3 --name lavavajillas # Secadora (1.5 horas) python scripts/optimize_appliance.py --duration 2 --name secadora ``` **Respuesta incluye:** - Hora óptima de inicio y fin - Coste total del ciclo (€) - Desglose de precio por hora - Ahorro vs usar en horario medio - Hasta 2 alternativas con <10% diferencia de coste ## Salida JSON Todos los scripts soportan `--json` para integración programática: ```bash python scripts/get_pvpc.py --json python scripts/find_cheap_ranges.py --json python scripts/optimize_appliance.py --duration 3 --json ``` ## Ejemplos de uso desde el agente **Usuario:** "¿Cuánto cuesta la luz ahora?" ```bash python scripts/get_pvpc.py --now python scripts/precio_referencia.py --now ``` **Usuario:** "¿Cuándo es más barata la luz hoy?" ```bash python scripts/find_cheap_ranges.py ``` **Usuario:** "¿Cuándo pongo la lavadora?" ```bash python scripts/optimize_appliance.py --duration 2 --name lavadora ``` **Usuario:** "¿Cuándo es valle?" ```bash python scripts/tarifa_periodos.py --now ``` **Usuario:** "¿Cuándo pongo el lavavajillas que dura 3 horas?" ```bash python scripts/optimize_appliance.py --duration 3 --name lavavajillas ``` ## Notas técnicas - **Fuente de datos:** API pública de ESIOS (Red Eléctrica de España) - **Tarifa:** PVPC 2.0TD (usuarios domésticos con potencia <10 kW) - **Actualización:** Los precios se publican diariamente alrededor de las 20:00 para el día siguiente - **Precios:** Incluyen todos los términos (energía, peajes, cargos) en €/kWh - **Sin autenticación:** Usa endpoint público, no requiere token ## Limitaciones - Los datos históricos no se almacenan localmente (cada consulta es fresh) - La clasificación ALTO/MEDIO/BAJO es relativa al día actual, no a históricos - Los festivos nacionales no se detectan automáticamente (se tratan como días laborables) - Requiere conectividad a internet para consultar la API

path: scripts/find_cheap_ranges.py
content: #!/usr/bin/env python3
"""
Encuentra rangos de horas con precios baratos
"""

import sys
import json
from get_pvpc import get_pvpc_data, get_stats

def find_cheap_hours(prices, percentile=30):
    """
    Encuentra horas con precio por debajo del percentil dado
    """
    if not prices:
        return []
    
    price_values = sorted([p['price'] for p in prices])
    threshold_idx = int(len(price_values) * percentile / 100)
    threshold = price_values[threshold_idx]
    
    cheap_hours = [p for p in prices if p['price'] <= threshold]
    return cheap_hours, threshold

def group_consecutive_hours(hours, tolerance=0.01):
    """
    Agrupa horas consecutivas con precios similares
    tolerance: diferencia máxima de precio para considerar similares (€/kWh)
    """
    if not hours:
        return []
    
    # Ordenar por hora
    sorted_hours = sorted(hours, key=lambda x: x['hour'])
    
    ranges = []
    current_range = [sorted_hours[0]]
    
    for i in range(1, len(sorted_hours)):
        prev = sorted_hours[i-1]
        curr = sorted_hours[i]
        
        # Agregar solo si es consecutiva Y precio similar
        if (curr['hour'] == prev['hour'] + 1 and 
            abs(curr['price'] - prev['price']) <= tolerance):
            current_range.append(curr)
        else:
            if len(current_range) >= 2:  # Solo rangos de 2+ horas
                ranges.append(current_range)
            current_range = [curr]
    
    # Añadir último rango
    if len(current_range) >= 2:
        ranges.append(current_range)
    
    return ranges

def format_range(hours_range):
    """Formatea un rango de horas para mostrar"""
    if not hours_range:
        return ""
    
    start = hours_range[0]['hour']
    last_hour = hours_range[-1]['hour']
    end = last_hour + 1
    
    # Si end es 24, mostrarlo como "al día siguiente"
    end_display = end if end < 24 else 0
    crosses_midnight = end >= 24
    
    min_price = min(h['price'] for h in hours_range)
    max_price = max(h['price'] for h in hours_range)
    avg_price = sum(h['price'] for h in hours_range) / len(hours_range)
    
    return {
        'start': start,
        'end': end_display,
        'end_raw': end,
        'crosses_midnight': crosses_midnight,
        'duration': len(hours_range),
        'min_price': round(min_price, 4),
        'max_price': round(max_price, 4),
        'avg_price': round(avg_price, 4)
    }

def main():
    import argparse
    
    parser = argparse.ArgumentParser(description='Encuentra horas baratas')
    parser.add_argument('--percentile', type=int, default=30, 
                       help='Percentil para considerar "barato" (default: 30)')
    parser.add_argument('--tolerance', type=float, default=0.01,
                       help='Diferencia máxima de precio para agrupar (€/kWh)')
    parser.add_argument('--json', action='store_true', help='Salida JSON')
    
    args = parser.parse_args()
    
    prices = get_pvpc_data()
    if not prices:
        print("No se pudieron obtener los datos", file=sys.stderr)
        sys.exit(1)
    
    stats = get_stats(prices)
    cheap_hours, threshold = find_cheap_hours(prices, args.percentile)
    ranges = group_consecutive_hours(cheap_hours, args.tolerance)
    
    if args.json:
        output = {
            'threshold': round(threshold, 4),
            'ranges': [format_range(r) for r in ranges],
            'stats': stats
        }
        print(json.dumps(output, indent=2, ensure_ascii=False))
    else:
        print(f"🔋 Horas baratas hoy (≤{args.percentile}% del día, ≤{threshold:.4f} €/kWh)\n")
        
        if not ranges:
            print("No hay rangos baratos consecutivos hoy")
            sys.exit(0)
        
        # Ordenar rangos por duración (más largo primero)
        ranges.sort(key=lambda r: len(r), reverse=True)
        
        for i, r in enumerate(ranges, 1):
            info = format_range(r)
            end_str = f"{info['end']:02d}:00"
            if info['crosses_midnight']:
                end_str += " (día siguiente)"
            print(f"{i}. {info['start']:02d}:00 - {end_str} ({info['duration']}h)")
            print(f"   💰 {info['min_price']:.4f} - {info['max_price']:.4f} €/kWh (media: {info['avg_price']:.4f})")
            
            # Calcular ahorro vs media
            saving = ((stats['mean'] - info['avg_price']) / stats['mean']) * 100
            print(f"   💚 Ahorro vs media: {saving:.1f}%\n")

if __name__ == '__main__':
    main()


path: scripts/get_pvpc.py
content: #!/usr/bin/env python3
"""
Consulta precios PVPC desde la API de ESIOS (Red Eléctrica de España)
Sin necesidad de token - usa endpoint público alternativo
"""

import json
import sys
from datetime import datetime, timedelta
from urllib.request import urlopen, Request
from urllib.error import URLError

def get_pvpc_data(date_str=None):
    """
    Obtiene datos PVPC para una fecha específica
    date_str: formato YYYY-MM-DD, por defecto hoy
    """
    if date_str is None:
        date_str = datetime.now().strftime('%Y-%m-%d')
    
    # API pública de ESIOS (no requiere token)
    url = f"https://api.esios.ree.es/archives/70/download_json?locale=es&date={date_str}"
    
    try:
        req = Request(url, headers={'User-Agent': 'Mozilla/5.0'})
        with urlopen(req, timeout=10) as response:
            data = json.loads(response.read().decode('utf-8'))
            return parse_pvpc_response(data)
    except URLError as e:
        print(f"Error consultando API: {e}", file=sys.stderr)
        return None
    except Exception as e:
        print(f"Error procesando datos: {e}", file=sys.stderr)
        return None

def parse_pvpc_response(data):
    """Parsea respuesta de la API y extrae precios por hora"""
    if 'PVPC' not in data:
        return None
    
    prices = []
    for entry in data['PVPC']:
        try:
            # Formato "00-01" -> hora 0, "01-02" -> hora 1, etc.
            hour_str = entry['Hora'].split('-')[0]
            hour = int(hour_str)
            if hour == 24:  # "24-01" del día siguiente
                continue
            # Precio en €/kWh (viene en €/MWh, dividir por 1000)
            price = float(entry['PCB'].replace(',', '.')) / 1000
            prices.append({'hour': hour, 'price': round(price, 4)})
        except (KeyError, ValueError) as e:
            continue
    
    return prices

def get_current_price(prices):
    """Obtiene el precio de la hora actual"""
    current_hour = datetime.now().hour
    for p in prices:
        if p['hour'] == current_hour:
            return p
    return None

def get_stats(prices):
    """Calcula estadísticas del día"""
    if not prices:
        return None
    
    price_values = [p['price'] for p in prices]
    return {
        'min': min(price_values),
        'max': max(price_values),
        'mean': round(sum(price_values) / len(price_values), 4),
        'min_hour': min(prices, key=lambda x: x['price'])['hour'],
        'max_hour': max(prices, key=lambda x: x['price'])['hour']
    }

def main():
    import argparse
    
    parser = argparse.ArgumentParser(description='Consulta precios PVPC')
    parser.add_argument('--date', help='Fecha YYYY-MM-DD (por defecto hoy)')
    parser.add_argument('--now', action='store_true', help='Muestra precio actual')
    parser.add_argument('--all', action='store_true', help='Muestra todos los precios del día')
    parser.add_argument('--json', action='store_true', help='Salida en formato JSON')
    
    args = parser.parse_args()
    
    prices = get_pvpc_data(args.date)
    
    if not prices:
        print("No se pudieron obtener los datos", file=sys.stderr)
        sys.exit(1)
    
    stats = get_stats(prices)
    
    if args.json:
        output = {
            'prices': prices,
            'stats': stats,
            'current': get_current_price(prices)
        }
        print(json.dumps(output, indent=2, ensure_ascii=False))
    elif args.now:
        current = get_current_price(prices)
        if current:
            print(f"Precio actual ({current['hour']:02d}:00): {current['price']:.4f} €/kWh")
            print(f"Mínimo hoy: {stats['min']:.4f} €/kWh ({stats['min_hour']:02d}:00)")
            print(f"Máximo hoy: {stats['max']:.4f} €/kWh ({stats['max_hour']:02d}:00)")
            print(f"Media hoy: {stats['mean']:.4f} €/kWh")
        else:
            print("No hay datos para la hora actual", file=sys.stderr)
            sys.exit(1)
    elif args.all:
        for p in prices:
            print(f"{p['hour']:02d}:00 - {p['price']:.4f} €/kWh")
    else:
        # Por defecto muestra resumen
        print(f"Resumen PVPC ({datetime.now().strftime('%d/%m/%Y')})")
        print(f"Min: {stats['min']:.4f} €/kWh ({stats['min_hour']:02d}:00)")
        print(f"Max: {stats['max']:.4f} €/kWh ({stats['max_hour']:02d}:00)")
        print(f"Media: {stats['mean']:.4f} €/kWh")

if __name__ == '__main__':
    main()


path: scripts/optimize_appliance.py
content: #!/usr/bin/env python3
"""
Encuentra el mejor momento para usar electrodomésticos
según duración y precios PVPC
"""

import sys
import json
from get_pvpc import get_pvpc_data, get_stats

def find_best_window(prices, duration):
    """
    Encuentra la ventana de N horas consecutivas con menor coste total
    """
    if not prices or duration > len(prices):
        return None
    
    # Ordenar por hora para asegurar secuencia
    sorted_prices = sorted(prices, key=lambda x: x['hour'])
    
    best_window = None
    best_cost = float('inf')
    
    for i in range(len(sorted_prices) - duration + 1):
        window = sorted_prices[i:i+duration]
        
        # Verificar que las horas son consecutivas
        if all(window[j]['hour'] == window[j-1]['hour'] + 1 
               for j in range(1, len(window))):
            
            total_cost = sum(h['price'] for h in window)
            
            if total_cost < best_cost:
                best_cost = total_cost
                best_window = window
    
    if best_window:
        return {
            'start': best_window[0]['hour'],
            'end': best_window[-1]['hour'] + 1,
            'hours': best_window,
            'total_cost': round(best_cost, 4),
            'avg_price': round(best_cost / duration, 4)
        }
    
    return None

def find_alternatives(prices, duration, best_window, threshold=1.10):
    """
    Encuentra ventanas alternativas dentro del threshold (ej: +10%)
    """
    if not best_window:
        return []
    
    sorted_prices = sorted(prices, key=lambda x: x['hour'])
    alternatives = []
    max_cost = best_window['total_cost'] * threshold
    
    for i in range(len(sorted_prices) - duration + 1):
        window = sorted_prices[i:i+duration]
        
        if all(window[j]['hour'] == window[j-1]['hour'] + 1 
               for j in range(1, len(window))):
            
            total_cost = sum(h['price'] for h in window)
            start_hour = window[0]['hour']
            
            # Excluir la mejor ventana y las que superan threshold
            if (start_hour != best_window['start'] and 
                total_cost <= max_cost):
                alternatives.append({
                    'start': start_hour,
                    'end': window[-1]['hour'] + 1,
                    'total_cost': round(total_cost, 4),
                    'diff_percent': round(((total_cost - best_window['total_cost']) / 
                                         best_window['total_cost']) * 100, 1)
                })
    
    # Ordenar por coste
    alternatives.sort(key=lambda x: x['total_cost'])
    return alternatives[:2]  # Top 2 alternativas

def main():
    import argparse
    from datetime import datetime
    
    parser = argparse.ArgumentParser(description='Optimiza uso de electrodomésticos')
    parser.add_argument('--duration', type=int, required=True,
                       help='Duración en horas del electrodoméstico')
    parser.add_argument('--name', default='electrodoméstico',
                       help='Nombre del electrodoméstico (ej: lavadora)')
    parser.add_argument('--alternatives', type=int, default=2,
                       help='Número de alternativas a mostrar')
    parser.add_argument('--json', action='store_true', help='Salida JSON')
    
    args = parser.parse_args()
    
    prices = get_pvpc_data()
    if not prices:
        print("No se pudieron obtener los datos", file=sys.stderr)
        sys.exit(1)
    
    stats = get_stats(prices)
    best = find_best_window(prices, args.duration)
    
    if not best:
        print(f"No se encontró ventana de {args.duration}h consecutivas", file=sys.stderr)
        sys.exit(1)
    
    alternatives = find_alternatives(prices, args.duration, best)
    
    if args.json:
        output = {
            'appliance': args.name,
            'duration': args.duration,
            'best': best,
            'alternatives': alternatives,
            'stats': stats
        }
        print(json.dumps(output, indent=2, ensure_ascii=False))
    else:
        emoji_map = {
            'lavadora': '🧺',
            'lavavajillas': '🍽️',
            'secadora': '👕',
            'horno': '🍕',
            'calefaccion': '🔥'
        }
        emoji = emoji_map.get(args.name.lower(), '⚡')
        
        print(f"{emoji} Mejor momento para {args.name} ({args.duration}h)\n")
        print(f"⏰ Inicio recomendado: {best['start']:02d}:00")
        print(f"⏱️  Finaliza: {best['end']:02d}:00\n")
        print(f"💰 Coste total: {best['total_cost']:.4f} €")
        print(f"📊 Precio medio: {best['avg_price']:.4f} €/kWh\n")
        
        print("📈 Desglose por hora:")
        for h in best['hours']:
            print(f"   • {h['hour']:02d}:00: {h['price']:.4f} €/kWh")
        
        # Calcular ahorro vs media
        cost_at_avg = stats['mean'] * args.duration
        saving = cost_at_avg - best['total_cost']
        saving_percent = (saving / cost_at_avg) * 100
        
        print(f"\n💚 Ahorro vs media del día: {saving:.4f} € ({saving_percent:.1f}%)")
        
        # Alternativas
        if alternatives:
            print(f"\n🔄 Alternativas:")
            for i, alt in enumerate(alternatives, 1):
                print(f"   {i}. {alt['start']:02d}:00-{alt['end']:02d}:00")
                print(f"      Coste: {alt['total_cost']:.4f} € (+{alt['diff_percent']}%)")

if __name__ == '__main__':
    main()


path: scripts/precio_referencia.py
content: #!/usr/bin/env python3
"""
Clasifica precios PVPC como ALTO/MEDIO/BAJO
basado en percentiles del día
"""

import sys
from get_pvpc import get_pvpc_data, get_stats, get_current_price

def classify_price(price, stats):
    """
    Clasifica un precio como ALTO/MEDIO/BAJO
    basado en percentiles del día
    
    BAJO: < percentil 30
    MEDIO: percentil 30-70
    ALTO: > percentil 70
    """
    mean = stats['mean']
    price_range = stats['max'] - stats['min']
    
    # Calcular percentiles aproximados
    p30 = stats['min'] + (price_range * 0.3)
    p70 = stats['min'] + (price_range * 0.7)
    
    if price <= p30:
        return {
            'level': 'BAJO',
            'emoji': '🟢',
            'description': 'Precio bajo (oportunidad de ahorro)'
        }
    elif price >= p70:
        return {
            'level': 'ALTO',
            'emoji': '🔴',
            'description': 'Precio alto (evita consumos no esenciales)'
        }
    else:
        return {
            'level': 'MEDIO',
            'emoji': '🟡',
            'description': 'Precio medio'
        }

def get_price_context(price, stats):
    """
    Genera contexto completo del precio actual
    """
    classification = classify_price(price, stats)
    
    # Calcular desviación respecto a la media
    deviation = ((price - stats['mean']) / stats['mean']) * 100
    
    # Posición en el rango del día
    position = ((price - stats['min']) / (stats['max'] - stats['min'])) * 100
    
    return {
        'classification': classification,
        'deviation_percent': round(deviation, 1),
        'position_percent': round(position, 1),
        'below_mean': price < stats['mean'],
        'stats': stats
    }

def main():
    import argparse
    from datetime import datetime
    
    parser = argparse.ArgumentParser(description='Clasifica precio PVPC')
    parser.add_argument('--price', type=float,
                       help='Precio a clasificar (€/kWh)')
    parser.add_argument('--now', action='store_true',
                       help='Clasifica precio actual')
    
    args = parser.parse_args()
    
    prices = get_pvpc_data()
    if not prices:
        print("No se pudieron obtener los datos", file=sys.stderr)
        sys.exit(1)
    
    stats = get_stats(prices)
    
    if args.now:
        current = get_current_price(prices)
        if not current:
            print("No hay datos para la hora actual", file=sys.stderr)
            sys.exit(1)
        price = current['price']
        hour = current['hour']
    elif args.price:
        price = args.price
        hour = None
    else:
        print("Usa --now o --price", file=sys.stderr)
        sys.exit(1)
    
    context = get_price_context(price, stats)
    cls = context['classification']
    
    print(f"{cls['emoji']} PRECIO {cls['level']}")
    if hour is not None:
        print(f"   {hour:02d}:00: {price:.4f} €/kWh")
    else:
        print(f"   {price:.4f} €/kWh")
    
    print(f"\n📊 Contexto del día:")
    print(f"   Media: {stats['mean']:.4f} €/kWh")
    print(f"   Mínimo: {stats['min']:.4f} €/kWh ({stats['min_hour']:02d}:00)")
    print(f"   Máximo: {stats['max']:.4f} €/kWh ({stats['max_hour']:02d}:00)")
    
    if context['below_mean']:
        print(f"\n💰 {abs(context['deviation_percent']):.1f}% por debajo de la media")
    else:
        print(f"\n💸 {context['deviation_percent']:.1f}% por encima de la media")
    
    print(f"📍 Posición: {context['position_percent']:.0f}% del rango diario")
    print(f"\n💡 {cls['description']}")

if __name__ == '__main__':
    main()


path: scripts/tarifa_periodos.py
content: #!/usr/bin/env python3
"""
Identifica periodos tarifarios 2.0TD (valle/llano/punta)
Ajustado por horario de verano/invierno
"""

import sys
from datetime import datetime

def is_summer_time():
    """
    Detecta si estamos en horario de verano (último domingo de marzo a último domingo de octubre)
    """
    now = datetime.now()
    month = now.month
    
    # Verano: abril a octubre (inclusive)
    # Invierno: noviembre a marzo
    return 4 <= month <= 10

def get_period_info(hour=None, weekday=None):
    """
    Devuelve el periodo tarifario para una hora y día de la semana dados
    hour: 0-23, si None usa hora actual
    weekday: 0=lunes...6=domingo, si None usa día actual
    
    Tarifa 2.0TD:
    - VALLE: 00:00-08:00 todos los días + sábados y domingos completos + festivos
    - LLANO: 08:00-10:00, 14:00-18:00, 22:00-00:00 (lun-vie)
    - PUNTA: 10:00-14:00, 18:00-22:00 (lun-vie)
    
    Nota: Los periodos son iguales en verano e invierno para la tarifa 2.0TD
    """
    if hour is None:
        hour = datetime.now().hour
    if weekday is None:
        weekday = datetime.now().weekday()
    
    # Sábado (5) o domingo (6) = todo el día valle
    if weekday in [5, 6]:
        return {
            'period': 'VALLE',
            'emoji': '🌙',
            'description': 'Fin de semana (todo el día valle)'
        }
    
    # Lunes a viernes
    if 0 <= hour < 8:
        return {
            'period': 'VALLE',
            'emoji': '🌙',
            'description': 'Periodo valle (00:00-08:00)'
        }
    elif 8 <= hour < 10 or 14 <= hour < 18 or 22 <= hour < 24:
        return {
            'period': 'LLANO',
            'emoji': '⚡',
            'description': 'Periodo llano'
        }
    else:  # 10-14 o 18-22
        return {
            'period': 'PUNTA',
            'emoji': '🔴',
            'description': 'Periodo punta'
        }

def get_next_valle():
    """Calcula cuándo empieza el próximo periodo valle"""
    now = datetime.now()
    current_hour = now.hour
    weekday = now.weekday()
    
    # Si es viernes tarde/noche, el siguiente valle es sábado todo el día
    if weekday == 4 and current_hour >= 8:
        return {
            'starts_in_hours': 24 - current_hour,
            'description': 'Sábado (todo el día valle)'
        }
    
    # Si es sábado o domingo, ya estamos en valle
    if weekday in [5, 6]:
        return {
            'starts_in_hours': 0,
            'description': 'Ahora (fin de semana)'
        }
    
    # Lunes a viernes
    if current_hour >= 8:
        # El siguiente valle es a las 00:00 del día siguiente
        hours_until = 24 - current_hour
        return {
            'starts_in_hours': hours_until,
            'description': '00:00-08:00 (mañana)'
        }
    else:
        # Estamos en valle ahora
        return {
            'starts_in_hours': 0,
            'description': 'Ahora (00:00-08:00)'
        }

def main():
    import argparse
    
    parser = argparse.ArgumentParser(description='Consulta periodos tarifarios 2.0TD')
    parser.add_argument('--now', action='store_true', 
                       help='Muestra periodo actual')
    parser.add_argument('--hour', type=int, 
                       help='Hora específica (0-23)')
    parser.add_argument('--weekday', type=int,
                       help='Día de la semana (0=lun, 6=dom)')
    parser.add_argument('--all', action='store_true',
                       help='Muestra todos los periodos del día')
    
    args = parser.parse_args()
    
    summer = is_summer_time()
    season = "verano ☀️" if summer else "invierno ❄️"
    
    if args.all:
        print(f"📅 Periodos tarifarios 2.0TD (horario de {season})\n")
        print("Lunes a Viernes:")
        print("  🌙 VALLE: 00:00-08:00")
        print("  ⚡ LLANO: 08:00-10:00, 14:00-18:00, 22:00-00:00")
        print("  🔴 PUNTA: 10:00-14:00, 18:00-22:00")
        print("\nSábados, Domingos y Festivos:")
        print("  🌙 VALLE: Todo el día (00:00-24:00)")
        print("\nNota: Los periodos son iguales en verano e invierno para 2.0TD")
    elif args.now or args.hour is not None:
        hour = args.hour if args.hour is not None else None
        weekday = args.weekday if args.weekday is not None else None
        
        period = get_period_info(hour, weekday)
        next_valle = get_next_valle()
        
        now = datetime.now()
        print(f"📅 {now.strftime('%d/%m/%Y %H:%M')} (horario de {season})\n")
        print(f"{period['emoji']} Periodo actual: {period['period']}")
        print(f"   {period['description']}\n")
        
        if next_valle['starts_in_hours'] > 0:
            print(f"🌙 Próximo valle: en {next_valle['starts_in_hours']}h")
            print(f"   {next_valle['description']}")
        else:
            print("🌙 Estás en periodo valle ahora")
    else:
        # Muestra info general
        print(f"Tarifa 2.0TD - Horario de {season}")
        print("\nPara ver periodos: --all")
        print("Para ver periodo actual: --now")

if __name__ == '__main__':
    main()


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: clean (LOW)