Inicio Rápido

Esta guía te ayudará a empezar a usar el SDK de Datadis en pocos minutos. El SDK incluye dos versiones del cliente: V1 (básico) y V2 (recomendado con manejo avanzado de errores).

Configuración inicial

Antes de comenzar, necesitas tener:

  1. Una cuenta activa en Datadis

  2. Tu NIF como nombre de usuario

  3. Tu contraseña de acceso

Nota

Importante: La API de Datadis solo acepta fechas en formato mensual (YYYY/MM). No es posible especificar días específicos.

Instalación

pip install ctr-datadis

Elección de Cliente: V1 vs V2

Cliente V1 (Básico)
  • Respuestas simples (listas directas)

  • Manejo básico de errores

  • Compatible con versiones anteriores

  • Ideal para scripts simples

Cliente V2 (Recomendado)
  • Respuestas estructuradas con información de errores por distribuidor

  • Manejo robusto de errores

  • Funcionalidad exclusiva: energía reactiva

  • Ideal para aplicaciones de producción

Primer ejemplo - Cliente V1

Ejemplo básico con el cliente V1:

from datadis_python.client.v1.simple_client import SimpleDatadisClientV1

# Crear cliente V1
client = SimpleDatadisClientV1(
    username="tu_nif",
    password="tu_contraseña"
)

# Obtener puntos de suministro
supplies = client.get_supplies()

for supply in supplies:
    print(f"CUPS: {supply.cups}")
    print(f"Dirección: {supply.address}")
    print(f"Distribuidor: {supply.distributor}")
    print("---")

# Cerrar cliente
client.close()

Primer ejemplo - Cliente V2 (Recomendado)

Ejemplo con manejo mejorado de errores usando V2:

from datadis_python.client.v2.simple_client import SimpleDatadisClientV2

# Crear cliente V2
with SimpleDatadisClientV2("tu_nif", "tu_contraseña") as client:
    # Obtener puntos de suministro con manejo de errores
    supplies_response = client.get_supplies()

    print(f"Suministros obtenidos: {len(supplies_response.supplies)}")

    # Verificar errores por distribuidor
    if supplies_response.distributor_error:
        print("Errores encontrados:")
        for error in supplies_response.distributor_error:
            print(f"- {error.distributorName}: {error.errorDescription}")

    # Procesar suministros válidos
    for supply in supplies_response.supplies:
        print(f"CUPS: {supply.cups}")
        print(f"Dirección: {supply.address}")
        print(f"Distribuidor: {supply.distributor}")
        print("---")

Obtener datos de consumo

Cliente V1 - Obtener consumo anual

from datadis_python.client.v1.simple_client import SimpleDatadisClientV1

with SimpleDatadisClientV1("tu_nif", "tu_contraseña") as client:
    # Obtener suministros
    supplies = client.get_supplies()

    if supplies:
        supply = supplies[0]  # Primer suministro

        # Obtener consumo de todo 2024 (formato mensual)
        consumption = client.get_consumption(
            cups=supply.cups,
            distributor_code=supply.distributorCode,
            date_from="2024/01",  # Enero 2024
            date_to="2024/12"     # Diciembre 2024
        )

        print(f"Registros de consumo: {len(consumption)}")

        # Calcular consumo total
        total_kwh = sum(c.consumptionKWh for c in consumption if c.consumptionKWh)
        print(f"Consumo total 2024: {total_kwh:.2f} kWh")

Cliente V2 - Obtener consumo con manejo de errores

from datadis_python.client.v2.simple_client import SimpleDatadisClientV2

with SimpleDatadisClientV2("tu_nif", "tu_contraseña") as client:
    # Obtener suministros
    supplies_response = client.get_supplies()

    if supplies_response.supplies:
        supply = supplies_response.supplies[0]

        # Obtener consumo con manejo robusto de errores
        consumption_response = client.get_consumption(
            cups=supply.cups,
            distributor_code=supply.distributorCode,
            date_from="2024/01",
            date_to="2024/12"
        )

        # Verificar errores específicos
        if consumption_response.distributor_error:
            print("Errores al obtener consumo:")
            for error in consumption_response.distributor_error:
                print(f"- {error.distributorName}: {error.errorDescription}")

        # Procesar datos válidos
        consumption = consumption_response.time_curve
        if consumption:
            total_kwh = sum(c.consumptionKWh for c in consumption if c.consumptionKWh)
            real_data = len([c for c in consumption if c.obtainMethod == "Real"])

            print(f"Consumo total 2024: {total_kwh:.2f} kWh")
            print(f"Datos reales: {real_data}/{len(consumption)} registros")

Ejemplo completo - Análisis de datos

Este ejemplo muestra cómo obtener y analizar todos los tipos de datos disponibles:

from datadis_python.client.v2.simple_client import SimpleDatadisClientV2
from datadis_python.exceptions import AuthenticationError, APIError, DatadisError

def analizar_datos_completos(username, password, year="2024"):
    """Análisis completo de datos de un año específico"""

    try:
        with SimpleDatadisClientV2(username, password) as client:
            print(f"Analizando datos del año {year}...")

            # 1. Obtener distribuidores disponibles
            distributors_response = client.get_distributors()
            distributor_codes = distributors_response.dist_existence_user.get("distributorCodes", [])
            print(f"Distribuidores disponibles: {len(distributor_codes)}")

            # 2. Obtener todos los puntos de suministro
            supplies_response = client.get_supplies()
            supplies = supplies_response.supplies
            print(f"Puntos de suministro: {len(supplies)}")

            if not supplies:
                print("No se encontraron puntos de suministro")
                return

            # Analizar cada suministro
            for i, supply in enumerate(supplies, 1):
                print(f"\n--- Analizando suministro {i}/{len(supplies)} ---")
                print(f"CUPS: {supply.cups}")
                print(f"Dirección: {supply.address}")
                print(f"Distribuidor: {supply.distributor}")

                try:
                    # 3. Obtener detalles del contrato
                    contract_response = client.get_contract_detail(
                        cups=supply.cups,
                        distributor_code=supply.distributorCode
                    )

                    if contract_response.contract:
                        contract = contract_response.contract[0]
                        print(f"Potencia contratada: {contract.contractedPowerkW} kW")
                        print(f"Tarifa: {contract.accessFare}")

                    # 4. Obtener datos de consumo del año
                    consumption_response = client.get_consumption(
                        cups=supply.cups,
                        distributor_code=supply.distributorCode,
                        date_from=f"{year}/01",
                        date_to=f"{year}/12"
                    )

                    consumption = consumption_response.time_curve
                    if consumption:
                        total_consumo = sum(c.consumptionKWh for c in consumption if c.consumptionKWh)
                        total_excedentes = sum(c.surplusEnergyKWh for c in consumption if c.surplusEnergyKWh)

                        print(f"Consumo total {year}: {total_consumo:.2f} kWh")
                        print(f"Excedentes generados: {total_excedentes:.2f} kWh")
                        print(f"Registros: {len(consumption)}")

                        # Verificar si tiene autoconsumo
                        tiene_autoconsumo = any(c.selfConsumptionKWh for c in consumption)
                        if tiene_autoconsumo:
                            total_autoconsumo = sum(c.selfConsumptionKWh for c in consumption if c.selfConsumptionKWh)
                            print(f"Autoconsumo: {total_autoconsumo:.2f} kWh")

                    # 5. Obtener potencias máximas
                    max_power_response = client.get_max_power(
                        cups=supply.cups,
                        distributor_code=supply.distributorCode,
                        date_from=f"{year}/01",
                        date_to=f"{year}/12"
                    )

                    if max_power_response.max_power:
                        potencias = [p.maxPower for p in max_power_response.max_power]
                        potencia_maxima_kw = max(potencias) / 1000
                        print(f"Potencia máxima registrada: {potencia_maxima_kw:.2f} kW")

                    # 6. Intentar obtener energía reactiva (solo V2)
                    try:
                        reactive_data = client.get_reactive_data(
                            cups=supply.cups,
                            distributor_code=supply.distributorCode,
                            date_from=f"{year}/01",
                            date_to=f"{year}/12"
                        )

                        if reactive_data:
                            print(f"Datos de energía reactiva: {len(reactive_data)} registros")
                        else:
                            print("Sin datos de energía reactiva")

                    except Exception as e:
                        print(f"Energía reactiva no disponible: {e}")

                except Exception as e:
                    print(f"Error procesando {supply.cups}: {e}")

            print(f"\nAnálisis completado para {len(supplies)} suministros")

    except AuthenticationError as e:
        print(f"Error de autenticación: {e}")
        print("Verifica tu NIF y contraseña")
    except APIError as e:
        print(f"Error de API: {e}")
    except DatadisError as e:
        print(f"Error de conexión: {e}")
    except Exception as e:
        print(f"Error inesperado: {e}")

# Ejecutar análisis
if __name__ == "__main__":
    analizar_datos_completos(
        username="tu_nif",
        password="tu_contraseña",
        year="2024"
    )

Manejo de errores específicos

El cliente V2 proporciona información detallada sobre errores por distribuidor:

from datadis_python.client.v2.simple_client import SimpleDatadisClientV2
from datadis_python.exceptions import AuthenticationError, APIError, DatadisError

def manejar_errores_avanzado():
    try:
        with SimpleDatadisClientV2("tu_nif", "tu_contraseña") as client:
            # Intentar obtener suministros
            supplies_response = client.get_supplies()

            # Analizar errores específicos por distribuidor
            if supplies_response.distributor_error:
                print("Errores por distribuidor:")
                for error in supplies_response.distributor_error:
                    print(f"Distribuidor: {error.distributorName}")
                    print(f"Código de error: {error.errorCode}")
                    print(f"Descripción: {error.errorDescription}")
                    print("---")

            # Procesar solo los suministros que se obtuvieron correctamente
            if supplies_response.supplies:
                print(f"Suministros válidos: {len(supplies_response.supplies)}")
                for supply in supplies_response.supplies:
                    print(f"- {supply.cups} ({supply.distributor})")
            else:
                print("No se pudieron obtener suministros de ningún distribuidor")

    except AuthenticationError:
        print("Credenciales inválidas. Verifica tu NIF y contraseña.")
    except APIError as e:
        print(f"Error de la API de Datadis: {e}")
    except DatadisError as e:
        print(f"Error de conexión o timeout: {e}")

Configuración avanzada

Puedes personalizar el comportamiento del cliente:

from datadis_python.client.v2.simple_client import SimpleDatadisClientV2

# Configuración para conexiones lentas o inestables
client = SimpleDatadisClientV2(
    username="tu_nif",
    password="tu_contraseña",
    timeout=240,  # Timeout extendido (default: 120 segundos)
    retries=5     # Más reintentos (default: 3)
)

# También funciona con context manager
with SimpleDatadisClientV2("tu_nif", "tu_contraseña", timeout=180, retries=4) as client:
    supplies_response = client.get_supplies()

Comparación V1 vs V2

Ejemplo que muestra las diferencias principales:

from datadis_python.client.v1.simple_client import SimpleDatadisClientV1
from datadis_python.client.v2.simple_client import SimpleDatadisClientV2

def comparar_versiones(username, password):
    print("=== Cliente V1 ===")
    try:
        with SimpleDatadisClientV1(username, password) as client_v1:
            # V1 devuelve una lista directa
            supplies_v1 = client_v1.get_supplies()
            print(f"Suministros V1: {len(supplies_v1)} (lista simple)")

            if supplies_v1:
                consumption_v1 = client_v1.get_consumption(
                    cups=supplies_v1[0].cups,
                    distributor_code=supplies_v1[0].distributorCode,
                    date_from="2024/01",
                    date_to="2024/03"
                )
                print(f"Consumo V1: {len(consumption_v1)} registros (lista simple)")
    except Exception as e:
        print(f"Error en V1: {e}")

    print("\n=== Cliente V2 ===")
    try:
        with SimpleDatadisClientV2(username, password) as client_v2:
            # V2 devuelve objetos de respuesta estructurados
            supplies_response_v2 = client_v2.get_supplies()
            print(f"Suministros V2: {len(supplies_response_v2.supplies)} (respuesta estructurada)")

            if supplies_response_v2.distributor_error:
                print(f"Errores detectados: {len(supplies_response_v2.distributor_error)}")

            if supplies_response_v2.supplies:
                consumption_response_v2 = client_v2.get_consumption(
                    cups=supplies_response_v2.supplies[0].cups,
                    distributor_code=supplies_response_v2.supplies[0].distributorCode,
                    date_from="2024/01",
                    date_to="2024/03"
                )
                print(f"Consumo V2: {len(consumption_response_v2.time_curve)} registros (respuesta estructurada)")

                # Funcionalidad exclusiva de V2
                try:
                    reactive_data = client_v2.get_reactive_data(
                        cups=supplies_response_v2.supplies[0].cups,
                        distributor_code=supplies_response_v2.supplies[0].distributorCode,
                        date_from="2024/01",
                        date_to="2024/03"
                    )
                    print(f"Energía reactiva V2: {len(reactive_data)} registros (solo en V2)")
                except Exception as e:
                    print(f"Energía reactiva no disponible: {e}")
    except Exception as e:
        print(f"Error en V2: {e}")

Casos de uso recomendados

Usa Cliente V1 cuando: - Necesites compatibilidad con código existente - Implementes scripts simples o prototipos rápidos - No requieras manejo avanzado de errores - Trabajes con un solo distribuidor conocido

Usa Cliente V2 cuando: - Desarrolles aplicaciones de producción - Necesites manejo robusto de errores por distribuidor - Quieras acceso a energía reactiva - Trabajes con múltiples distribuidores - Requieras información detallada de fallos

Próximos pasos