Referencia de la API
Esta sección documenta todas las clases y métodos disponibles en el SDK de Datadis.
Cliente Principal
Cliente V1 (Recomendado)
- class datadis_python.client.v1.simple_client.SimpleDatadisClientV1(username, password, timeout=120, retries=3)[fuente]
Bases:
objectCliente simplificado para la API V1 de Datadis.
Este cliente proporciona una interfaz fácil de usar para acceder a los datos de consumo eléctrico almacenados en las bases de datos de las distribuidoras eléctricas españolas a través de la plataforma Datadis.
Características principales: - Autenticación automática y renovación de tokens - Manejo robusto de timeouts y reintentos - Validación de datos con Pydantic para mayor seguridad - Soporte para tipos flexibles en parámetros de entrada - Context manager para gestión automática de recursos
Ejemplo
Uso básico del cliente:
from datadis_python.client.v1 import SimpleDatadisClientV1 # Inicializar el cliente client = SimpleDatadisClientV1( username="12345678A", password="mi_password", timeout=120, retries=3 ) # Usar como context manager (recomendado) with SimpleDatadisClientV1("12345678A", "mi_password") as client: # Obtener suministros supplies = client.get_supplies() # Obtener consumo para un suministro específico if supplies: consumption = client.get_consumption( cups=supplies[0].cups, distributor_code=supplies[0].distributorCode, date_from="2024/01", date_to="2024/12" )
- Parámetros:
username (str) – NIF del usuario registrado en Datadis (ej: «12345678A»)
password (str) – Contraseña de acceso a la plataforma Datadis
timeout (int) – Timeout para requests HTTP en segundos. 120s por defecto debido a la lentitud característica de la API de Datadis
retries (int) – Número de reintentos automáticos en caso de fallos de red o timeouts. 3 intentos por defecto
- Muestra:
AuthenticationError – Si las credenciales proporcionadas son inválidas
DatadisError – Si ocurren errores de conexión o de la API
ValidationError – Si los datos devueltos por la API no pasan la validación Pydantic
- authenticate()[fuente]
Autentica con la API de Datadis y obtiene el token de acceso.
Realiza una petición POST al endpoint
/nikola-auth/tokens/loginde la API de Datadis para obtener un token Bearer que será utilizado en todas las peticiones subsecuentes. El token se almacena automáticamente y se añade a los headers de la sesión HTTP.Nota
Este método normalmente NO necesita ser llamado manualmente, ya que la autenticación se realiza automáticamente cuando se ejecuta cualquier método que requiera acceso a la API (como
get_supplies(),get_consumption(), etc.).Advertencia
Los tokens de Datadis tienen expiración, pero la renovación se maneja automáticamente cuando se detecta un error 401 (Unauthorized).
Ejemplo
Autenticación manual (opcional):
client = SimpleDatadisClientV1("12345678A", "mi_password") # Verificar credenciales antes de hacer operaciones if client.authenticate(): print("Credenciales válidas") supplies = client.get_supplies() else: print("Error en las credenciales")
- Devuelve:
Truesi la autenticación fue exitosa,Falseen caso contrario- Tipo del valor devuelto:
- Muestra:
AuthenticationError – Si las credenciales (NIF/contraseña) son inválidas o el servidor devuelve un error de autenticación
DatadisError – Si ocurre un timeout o error de conexión con el servidor
Ver también
Documentación oficial:
POST /nikola-auth/tokens/loginLos tokens son válidos por tiempo limitado y se renuevan automáticamente
- get_supplies(authorized_nif=None, distributor_code=None)[fuente]
Obtiene la lista de puntos de suministro (CUPS) asociados al usuario.
Consulta el endpoint
GET /api-private/api/get-suppliespara obtener todos los puntos de suministro eléctrico que el usuario tiene registrados en Datadis. Los datos se validan automáticamente usando Pydantic para garantizar la integridad y consistencia de la información.Los puntos de suministro incluyen información detallada como: - Código CUPS (identificador único del punto de suministro) - Dirección física del suministro - Código postal, provincia y municipio - Distribuidor eléctrico y su código - Fechas de validez del contrato - Tipo de punto de medida (1-5)
- Códigos de distribuidor más comunes:
1: Viesgo
2: E-distribución (Endesa)
3: E-redes
4: ASEME
5: UFD (Naturgy)
6: EOSA
7: CIDE
8: IDE
Ejemplo
Obtener todos los suministros del usuario:
with SimpleDatadisClientV1("12345678A", "password") as client: supplies = client.get_supplies() for supply in supplies: print(f"CUPS: {supply.cups}") print(f"Dirección: {supply.address}") print(f"Distribuidor: {supply.distributor} (código: {supply.distributorCode})") print(f"Tipo: {supply.pointType}") print("---")
Filtrar por distribuidor específico:
# Solo suministros de Endesa (E-distribución) supplies_endesa = client.get_supplies(distributor_code=2) # También funciona con string supplies_endesa = client.get_supplies(distributor_code="2")
- Parámetros:
authorized_nif (Optional[str]) – NIF de una persona autorizada para consultar sus suministros. Si se especifica, se obtendrán los suministros de esa persona en lugar de los del usuario autenticado
distributor_code (Optional[Union[str, int]]) – Código del distribuidor para filtrar suministros. Acepta tanto enteros como strings (ej: 2 o «2»)
- Devuelve:
Lista de objetos
SupplyDatavalidados con Pydantic, cada uno representando un punto de suministro con toda su información asociada- Tipo del valor devuelto:
List[SupplyData]
- Muestra:
AuthenticationError – Si las credenciales son inválidas o el token expira
APIError – Si la API devuelve un error HTTP (400, 403, 404, 500, etc.)
DatadisError – Si ocurren errores de conexión o timeouts repetidos
ValidationError – Si los datos devueltos no pasan la validación Pydantic
Ver también
Documentación oficial:
GET /api-private/api/get-suppliesPara obtener detalles del contrato:
get_contract_detail()Lista completa de códigos de distribuidor en la documentación de la API
- get_distributors()[fuente]
Obtiene la lista de distribuidores eléctricos donde el usuario tiene suministros.
Consulta el endpoint
GET /api-private/api/get-distributors-with-suppliespara obtener información sobre las distribuidoras eléctricas donde el usuario autenticado tiene puntos de suministro activos. Esto es útil para conocer qué distribuidores están disponibles antes de realizar consultas específicas.La respuesta incluye los códigos únicos de cada distribuidor, que son necesarios para realizar consultas posteriores como obtener consumos, contratos o potencias máximas de suministros específicos.
- Distribuidores eléctricos en España:
Código 1: Viesgo (Cantabria, Asturias)
Código 2: E-distribución/Endesa (Nacional)
Código 3: E-redes (Galicia)
Código 4: ASEME (Melilla)
Código 5: UFD/Naturgy (Nacional)
Código 6: EOSA (Aragón)
Código 7: CIDE (Ceuta)
Código 8: IDE (Baleares)
Ejemplo
Listar distribuidores del usuario:
with SimpleDatadisClientV1("12345678A", "password") as client: distributors = client.get_distributors() print("Distribuidores con suministros:") for dist in distributors: print(f"- {dist.distributorName} (Código: {dist.distributorCode})") # Usar el primer distribuidor para consultas posteriores if distributors: first_dist_code = distributors[0].distributorCode supplies = client.get_supplies(distributor_code=first_dist_code)
- Devuelve:
Lista de objetos
DistributorDatavalidados con Pydantic. Cada objeto contiene el código y nombre del distribuidor donde el usuario tiene suministros activos- Tipo del valor devuelto:
List[DistributorData]
- Muestra:
AuthenticationError – Si las credenciales son inválidas o el token expira
APIError – Si la API devuelve un error HTTP (400, 403, 404, 500, etc.)
DatadisError – Si ocurren errores de conexión o timeouts repetidos
ValidationError – Si los datos devueltos no pasan la validación Pydantic
Ver también
Documentación oficial:
GET /api-private/api/get-distributors-with-suppliesPara filtrar suministros por distribuidor:
get_supplies()Los códigos obtenidos aquí se usan en
get_consumption(),get_contract_detail(), etc.
Nota
Solo se devuelven distribuidores donde el usuario tiene suministros activos. Si no hay suministros registrados, la lista estará vacía.
- get_contract_detail(cups, distributor_code)[fuente]
Obtiene los detalles del contrato eléctrico para un punto de suministro específico.
Consulta el endpoint
GET /api-private/api/get-contract-detailpara obtener información detallada del contrato asociado a un CUPS específico. Los datos incluyen información técnica, comercial y administrativa del suministro eléctrico.- Información del contrato incluida:
Datos básicos: CUPS, distribuidor, comercializadora
Características técnicas: Tensión, potencia contratada, tipo de punto
Datos tarifarios: Tarifa de acceso, discriminación horaria, código de tarifa
Ubicación: Provincia, municipio, código postal
Fechas: Inicio y fin de contrato, fechas de cambios
Autoconsumo: Tipo y características (si aplica)
Control de potencia: ICP o Maxímetro
CAU: Código de Autoconsumo Único (si es aplicable)
- Types de punto de medida:
Tipo 1: > 450 kW (alta tensión)
Tipo 2: 50-450 kW (media tensión)
Tipo 3: 15-50 kW (baja tensión)
Tipo 4: < 15 kW con discriminación horaria
Tipo 5: < 15 kW sin discriminación horaria
Ejemplo
Obtener detalles de contrato:
with SimpleDatadisClientV1("12345678A", "password") as client: # Primero obtener suministros supplies = client.get_supplies() if supplies: supply = supplies[0] # Obtener detalles del contrato contracts = client.get_contract_detail( cups=supply.cups, distributor_code=supply.distributorCode ) for contract in contracts: print(f"CUPS: {contract.cups}") print(f"Distribuidor: {contract.distributor}") print(f"Potencia contratada: {contract.contractedPowerkW} kW") print(f"Tarifa: {contract.accessFare}") print(f"Tipo de punto: {contract.pointType}") if contract.marketer: print(f"Comercializadora: {contract.marketer}")
Usando tipos flexibles de parámetros:
# Ambos formatos son válidos contracts1 = client.get_contract_detail("ES001234567890123456AB", 2) contracts2 = client.get_contract_detail("ES001234567890123456AB", "2")
- Parámetros:
- Devuelve:
Lista de objetos
ContractDatavalidados con Pydantic. Normalmente contiene un solo contrato, pero pueden ser varios si ha habido cambios históricos en el suministro- Tipo del valor devuelto:
List[ContractData]
- Muestra:
AuthenticationError – Si las credenciales son inválidas o el token expira
APIError – Si la API devuelve un error HTTP (400, 403, 404, 500, etc.)
DatadisError – Si ocurren errores de conexión o timeouts repetidos
ValidationError – Si los datos devueltos no pasan la validación Pydantic
ValueError – Si el CUPS no tiene el formato correcto (22 caracteres)
Ver también
Documentación oficial:
GET /api-private/api/get-contract-detailPara obtener la lista de CUPS:
get_supplies()Para datos de consumo del contrato:
get_consumption()
Nota
El CUPS debe ser exactamente de 22 caracteres alfanuméricos. Si el formato es incorrecto, la API devolverá un error.
- get_consumption(cups, distributor_code, date_from, date_to, measurement_type=0, point_type=None)[fuente]
Obtiene los datos de consumo eléctrico para un punto de suministro específico.
Consulta el endpoint
GET /api-private/api/get-consumption-datapara obtener la curva de carga (datos de consumo) de un CUPS en un período determinado. Los datos incluyen consumo, excedentes, generación y autoconsumo cuando aplique.- IMPORTANTE - Limitación de fechas:
La API de Datadis SOLO acepta fechas en formato mensual (YYYY/MM). NO se permiten fechas con días específicos. El SDK convierte automáticamente fechas datetime/date al formato requerido.
- IMPORTANTE - Disponibilidad de datos cuarto-horarios:
Los datos cada 15 minutos (
measurement_type=1) solo están disponibles para: - Tipos de punto 1 y 2 (alta y media tensión) - Tipo de punto 3 en E-distribución únicamente - Para el resto de casos, solo están disponibles datos horarios (measurement_type=0)- Tipos de medición:
0: Datos horarios (disponible para todos los tipos de punto)
1: Datos cuarto-horarios/15 min (limitado según tipo de punto)
- Tipos de punto de medida:
1: > 450 kW - Datos horarios y cuarto-horarios
2: 50-450 kW - Datos horarios y cuarto-horarios
3: 15-50 kW - Cuarto-horarios solo en E-distribución
4: < 15 kW con DH - Solo datos horarios
5: < 15 kW sin DH - Solo datos horarios
- Datos incluidos en la respuesta:
Consumo: Energía consumida de la red (kWh)
Excedentes: Energía vertida a la red (kWh)
Generación: Energía generada por autoconsumo (kWh)
Autoconsumo: Energía autoconsumida directamente (kWh)
Método de obtención: Real (medido) o Estimada (calculada)
Ejemplo
Obtener consumo horario para todo un año:
with SimpleDatadisClientV1("12345678A", "password") as client: supplies = client.get_supplies() if supplies: supply = supplies[0] # Consumo horario del año 2024 consumption = client.get_consumption( cups=supply.cups, distributor_code=supply.distributorCode, date_from="2024/01", # Enero 2024 date_to="2024/12", # Diciembre 2024 measurement_type=0 # Datos horarios ) # Analizar los datos total_kwh = sum(c.consumptionKWh for c in consumption if c.consumptionKWh) print(f"Consumo total 2024: {total_kwh:.2f} kWh") # Filtrar datos reales vs estimados real_data = [c for c in consumption if c.obtainMethod == "Real"] print(f"Datos reales: {len(real_data)}/{len(consumption)}")
Obtener datos cuarto-horarios (solo para tipos 1, 2, y 3 en E-distribución):
# Solo si el tipo de punto lo permite consumption_15min = client.get_consumption( cups="ES001234567890123456AB", distributor_code=2, # E-distribución date_from="2024/06", date_to="2024/06", measurement_type=1, # Cuarto-horarios point_type=3 # Tipo de punto )
Usando diferentes tipos de fecha:
from datetime import date, datetime # Con strings (recomendado) consumption1 = client.get_consumption(cups, dist_code, "2024/01", "2024/03") # Con objetos datetime (se convertirán automáticamente) start_date = datetime(2024, 1, 1) end_date = datetime(2024, 3, 31) consumption2 = client.get_consumption(cups, dist_code, start_date, end_date)
- Parámetros:
cups (str) – Código CUPS del punto de suministro (22 caracteres alfanuméricos)
distributor_code (Union[str, int]) – Código del distribuidor eléctrico (acepta int o str)
date_from (Union[str, datetime, date]) – Fecha de inicio en formato YYYY/MM. También acepta objetos datetime/date que se convertirán automáticamente
date_to (Union[str, datetime, date]) – Fecha de fin en formato YYYY/MM. También acepta objetos datetime/date que se convertirán automáticamente
measurement_type (Union[int, float, str]) – Tipo de medición - 0: horarios (defecto), 1: cuarto-horarios. Acepta int, float o str
point_type (Optional[Union[int, float, str]]) – Tipo de punto de medida (1-5). Requerido para datos cuarto-horarios. Acepta int, float o str
- Devuelve:
Lista de objetos
ConsumptionDatavalidados con Pydantic. Cada objeto representa una lectura de consumo con timestamp- Tipo del valor devuelto:
List[ConsumptionData]
- Muestra:
AuthenticationError – Si las credenciales son inválidas o el token expira
APIError – Si la API devuelve un error HTTP (400, 403, 404, 500, etc.)
DatadisError – Si ocurren errores de conexión o timeouts repetidos
ValidationError – Si las fechas no están en formato mensual válido o los datos no pasan la validación Pydantic
ValueError – Si se solicitan datos cuarto-horarios para un tipo de punto no compatible
Ver también
Documentación oficial:
GET /api-private/api/get-consumption-dataPara obtener CUPS:
get_supplies()Para datos de potencia máxima:
get_max_power()
Advertencia
Los datos cuarto-horarios no están disponibles para todos los tipos de punto. Verifique la compatibilidad antes de solicitar
measurement_type=1.
- get_max_power(cups, distributor_code, date_from, date_to)[fuente]
Obtiene los datos de potencia máxima demandada para un punto de suministro.
Consulta el endpoint
GET /api-private/api/get-max-powerpara obtener las potencias máximas registradas en cada período tarifario durante el rango de fechas especificado. Esta información es crucial para optimizar la potencia contratada y evitar penalizaciones por excesos.- IMPORTANTE - Limitación de fechas:
Al igual que los datos de consumo, la API SOLO acepta fechas en formato mensual (YYYY/MM). El SDK convierte automáticamente fechas datetime/date al primer día del mes correspondiente.
- Períodos tarifarios incluidos:
VALLE: Período de menor coste energético (madrugada)
LLANO: Período de coste intermedio (mañana/tarde)
PUNTA: Período de mayor coste energético (mediodía/noche)
P1, P2, P3, P4, P5, P6: Períodos específicos según tarifa
La potencia se mide en Vatios (W) y representa la demanda máxima registrada en cada período durante el mes consultado. Esta información es especialmente útil para:
Optimizar la potencia contratada
Identificar picos de consumo
Evaluar la eficiencia energética
Planificar instalaciones de autoconsumo
Ejemplo
Obtener potencias máximas del último año:
with SimpleDatadisClientV1("12345678A", "password") as client: supplies = client.get_supplies() if supplies: supply = supplies[0] # Potencias máximas de 2024 max_powers = client.get_max_power( cups=supply.cups, distributor_code=supply.distributorCode, date_from="2024/01", date_to="2024/12" ) # Analizar potencias por período periods = {} for power in max_powers: period = power.period if period not in periods: periods[period] = [] periods[period].append(power.maxPower) # Mostrar potencia máxima por período for period, powers in periods.items(): max_power_w = max(powers) max_power_kw = max_power_w / 1000 print(f"Período {period}: {max_power_kw:.2f} kW")
Identificar el mes con mayor demanda:
# Agrupar por mes monthly_max = {} for power in max_powers: month = power.date[:7] # YYYY/MM if month not in monthly_max: monthly_max[month] = 0 monthly_max[month] = max(monthly_max[month], power.maxPower) # Encontrar el mes de mayor demanda peak_month = max(monthly_max, key=monthly_max.get) peak_power_kw = monthly_max[peak_month] / 1000 print(f"Mayor demanda: {peak_power_kw:.2f} kW en {peak_month}")
- Parámetros:
cups (str) – Código CUPS del punto de suministro (22 caracteres alfanuméricos)
distributor_code (Union[str, int]) – Código del distribuidor eléctrico (acepta int o str)
date_from (Union[str, datetime, date]) – Fecha de inicio en formato YYYY/MM. También acepta objetos datetime/date que se convertirán automáticamente
date_to (Union[str, datetime, date]) – Fecha de fin en formato YYYY/MM. También acepta objetos datetime/date que se convertirán automáticamente
- Devuelve:
Lista de objetos
MaxPowerDatavalidados con Pydantic. Cada objeto representa la potencia máxima registrada en un período específico con fecha, hora y período tarifario- Tipo del valor devuelto:
List[MaxPowerData]
- Muestra:
AuthenticationError – Si las credenciales son inválidas o el token expira
APIError – Si la API devuelve un error HTTP (400, 403, 404, 500, etc.)
DatadisError – Si ocurren errores de conexión o timeouts repetidos
ValidationError – Si las fechas no están en formato mensual válido o los datos no pasan la validación Pydantic
ValueError – Si el CUPS no tiene el formato correcto
Ver también
Documentación oficial:
GET /api-private/api/get-max-powerPara obtener CUPS:
get_supplies()Para datos de consumo:
get_consumption()Para detalles del contrato:
get_contract_detail()
Nota
Las potencias se devuelven en Vatios (W). Para obtener kilovatios (kW) divida el valor entre 1000.
- close()[fuente]
Cierra la sesión HTTP y libera recursos del cliente.
Este método limpia los recursos utilizados por el cliente: - Cierra la sesión HTTP de requests - Elimina el token de autenticación almacenado - Libera las conexiones de red activas
Es una buena práctica llamar a este método cuando termine de usar el cliente, aunque se recomienda usar el cliente como context manager con
withpara gestión automática de recursos.Ejemplo
Uso manual (no recomendado):
client = SimpleDatadisClientV1("12345678A", "password") try: supplies = client.get_supplies() # ... más operaciones finally: client.close() # Limpiar recursos
Uso recomendado con context manager:
with SimpleDatadisClientV1("12345678A", "password") as client: supplies = client.get_supplies() # ... más operaciones # close() se llama automáticamente
Nota
Si usa el cliente como context manager (con
with), este método se llama automáticamente al salir del bloque.
- __enter__()[fuente]
Entrada del context manager para gestión automática de recursos.
Este método se llama automáticamente cuando se usa el cliente con la declaración
with. Permite usar el patrón context manager para garantizar la limpieza automática de recursos.Ejemplo
Uso como context manager (recomendado):
with SimpleDatadisClientV1("12345678A", "password") as client: # El método __enter__ se llama aquí automáticamente supplies = client.get_supplies() consumption = client.get_consumption(...) # ... más operaciones # El método __exit__ se llama automáticamente al salir
- Devuelve:
La propia instancia del cliente para usar en el bloque
with- Tipo del valor devuelto:
Ver también
__exit__()- Método de salida del context managerclose()- Limpieza manual de recursos
- __exit__(exc_type, exc_val, exc_tb)[fuente]
Salida del context manager con limpieza automática de recursos.
Este método se llama automáticamente al salir del bloque
with, garantizando que los recursos se liberen correctamente incluso si ocurre una excepción durante la ejecución.La limpieza incluye: - Cierre de la sesión HTTP - Eliminación del token de autenticación - Liberación de conexiones de red
Ejemplo
El context manager maneja automáticamente las excepciones:
try: with SimpleDatadisClientV1("12345678A", "password") as client: supplies = client.get_supplies() # Si ocurre una excepción aquí... raise Exception("Algo salió mal") except Exception as e: # ... los recursos se limpian automáticamente print(f"Error: {e}") # El cliente ya está cerrado y los recursos liberados
- Parámetros:
exc_type (Optional[type]) – Tipo de excepción que causó la salida (None si no hay excepción)
exc_val (Optional[BaseException]) – Instancia de la excepción (None si no hay excepción)
exc_tb (Optional[TracebackType]) – Traceback de la excepción (None si no hay excepción)
- Devuelve:
None (no suprime excepciones)
- Tipo del valor devuelto:
None
Ver también
__enter__()- Método de entrada del context managerclose()- Método de limpieza llamado internamenteNota
Este método no suprime excepciones - si ocurre un error en el bloque
with, la excepción se propagará normalmente después de la limpieza.
Cliente V2 (Experimental)
- class datadis_python.client.v2.simple_client.SimpleDatadisClientV2(username, password, timeout=120, retries=3)[fuente]
Bases:
objectCliente simplificado para la API V2 de Datadis con manejo mejorado de errores.
Este cliente implementa la versión 2 de la API de Datadis, que incluye mejoras significativas sobre la V1, especialmente en el manejo de errores por distribuidor y estructuras de respuesta más robustas. La V2 es la versión recomendada para nuevas implementaciones.
- Principales diferencias con V1:
Manejo de errores por distribuidor: Respuestas incluyen
distributorErrorEstructuras de respuesta mejoradas: Objetos tipados (SuppliesResponse, etc.)
Nueva funcionalidad: Datos de energía reactiva (
get_reactive_data())Validación mejorada: Validaciones específicas para types y rangos
Compatibilidad: Mantiene la misma interfaz simple que V1
- Características principales:
Autenticación automática y renovación de tokens
Manejo robusto de timeouts y reintentos con backoff exponencial
Validación de datos con Pydantic para máxima seguridad
Soporte para tipos flexibles en parámetros de entrada
Context manager para gestión automática de recursos
Manejo de errores por distribuidor (exclusivo de V2)
Respuestas estructuradas con información de errores detallada
- Ventajas de la API V2:
Robustez: Mejor manejo de fallos de distribuidores específicos
Información detallada: Códigos y descripciones de errores por distribuidor
Funcionalidad extendida: Acceso a datos de energía reactiva
Compatibilidad futura: Preparada para nuevas funcionalidades
Nota
La API V2 acepta las mismas limitaciones que V1: solo fechas en formato mensual (YYYY/MM) y disponibilidad limitada de datos cuarto-horarios.
Ejemplo
Uso básico con manejo de errores V2:
from datadis_python.client.v2 import SimpleDatadisClientV2 with SimpleDatadisClientV2("12345678A", "mi_password") as client: # Obtener suministros con información de errores supplies_response = client.get_supplies() print(f"Suministros obtenidos: {len(supplies_response.supplies)}") # Verificar errores por distribuidor if supplies_response.distributor_error: for error in supplies_response.distributor_error: print(f"Error en {error.distributorName}: {error.errorDescription}") # Trabajar con los suministros válidos for supply in supplies_response.supplies: print(f"CUPS: {supply.cups} - Distribuidor: {supply.distributor}")
Comparación con V1:
# V1 - Lista simple supplies_v1 = client_v1.get_supplies() # List[SupplyData] # V2 - Respuesta estructurada con manejo de errores supplies_v2 = client_v2.get_supplies() # SuppliesResponse actual_supplies = supplies_v2.supplies # List[SupplyData] errors = supplies_v2.distributor_error # List[DistributorError]
Funcionalidad exclusiva de V2 - Energía reactiva:
reactive_data = client.get_reactive_data( cups="ES001234567890123456AB", distributor_code="2", date_from="2024/01", date_to="2024/12" )
- Parámetros:
username (str) – NIF del usuario registrado en Datadis (ej: «12345678A»)
password (str) – Contraseña de acceso a la plataforma Datadis
timeout (int) – Timeout para requests HTTP en segundos. 120s por defecto debido a la lentitud característica de la API de Datadis
retries (int) – Número de reintentos automáticos en caso de fallos de red o timeouts. 3 intentos por defecto
- Muestra:
AuthenticationError – Si las credenciales proporcionadas son inválidas
DatadisError – Si ocurren errores de conexión o de la API
ValidationError – Si los datos devueltos no pasan la validación Pydantic
Added in version 2.0: Soporte para estructuras de respuesta mejoradas y manejo de errores por distribuidor
Ver también
SimpleDatadisClientV1- Versión anterior con respuestas simplesPara migración de V1 a V2: Las interfaces son compatibles, solo cambian los tipos de retorno
- __init__(username, password, timeout=120, retries=3)[fuente]
Inicializa el cliente simplificado V2.
- authenticate()[fuente]
Autentica con la API de Datadis y obtiene el token de acceso para V2.
Realiza una petición POST al endpoint
/nikola-auth/tokens/loginpara obtener un token Bearer válido para la API V2. El proceso de autenticación es idéntico entre V1 y V2, pero el token obtenido es compatible con ambas versiones de endpoints.Nota
Este método normalmente NO necesita ser llamado manualmente. La autenticación se realiza automáticamente en la primera petición que requiera acceso a la API. La V2 utiliza el mismo sistema de autenticación que V1.
Advertencia
Los tokens tienen expiración limitada, pero la renovación automática está implementada para todos los endpoints V2 cuando se detecta un error 401.
Ejemplo
Verificación manual de credenciales (opcional):
client = SimpleDatadisClientV2("12345678A", "mi_password") try: if client.authenticate(): print("✓ Credenciales válidas para API V2") # Ahora se pueden hacer peticiones autenticadas supplies_response = client.get_supplies() print(f"Suministros encontrados: {len(supplies_response.supplies)}") else: print("✗ Error en autenticación") except AuthenticationError as e: print(f"Error de credenciales: {e}") except DatadisError as e: print(f"Error de conexión: {e}")
- Devuelve:
Truesi la autenticación fue exitosa,Falseen caso contrario- Tipo del valor devuelto:
- Muestra:
AuthenticationError – Si las credenciales (NIF/contraseña) son inválidas, el servidor devuelve un error de autenticación, o la respuesta del servidor está vacía
DatadisError – Si ocurre un timeout durante la autenticación o error de conexión
Ver también
Documentación oficial:
POST /nikola-auth/tokens/loginLos tokens son válidos para ambas versiones V1 y V2 de la API
Renovación automática implementada en
_make_authenticated_request()
Nota
El token obtenido se almacena automáticamente en
self.tokeny se añade a los headers de la sesión HTTP comoAuthorization: Bearer <token>.
- get_supplies(authorized_nif=None, distributor_code=None)[fuente]
Obtiene la lista de puntos de suministro con manejo mejorado de errores (V2).
Consulta el endpoint
GET /api-private/api/get-supplies-v2para obtener todos los puntos de suministro eléctrico con manejo mejorado de errores por distribuidor. A diferencia de V1, la V2 devuelve una respuesta estructurada que incluye tanto los suministros válidos como información detallada de errores por cada distribuidor.- Ventajas de V2 sobre V1:
Manejo de errores por distribuidor: Información detallada de fallos específicos
Respuesta estructurada:
SuppliesResponseconsuppliesydistributor_errorMayor robustez: Operación exitosa aunque algunos distribuidores fallen
Información diagnóstica: Códigos y descripciones de errores específicos
- La respuesta incluye:
supplies: Lista de puntos de suministro válidos (idénticos a V1)
- distributor_error: Lista de errores específicos por distribuidor
distributorCode: Código del distribuidor con problemasdistributorName: Nombre del distribuidorerrorCode: Código específico del errorerrorDescription: Descripción detallada del problema
Ejemplo
Uso básico con manejo de errores V2:
with SimpleDatadisClientV2("12345678A", "password") as client: response = client.get_supplies() print(f"✓ Suministros obtenidos: {len(response.supplies)}") # Mostrar información de suministros for supply in response.supplies: print(f"CUPS: {supply.cups}") print(f"Distribuidor: {supply.distributor} (código: {supply.distributorCode})") print(f"Dirección: {supply.address}") print("---") # Verificar y manejar errores por distribuidor if response.distributor_error: print(f"⚠️ Errores encontrados en {len(response.distributor_error)} distribuidores:") for error in response.distributor_error: print(f"- {error.distributorName} ({error.distributorCode}): {error.errorDescription}") else: print("✓ Todos los distribuidores respondieron correctamente")
Comparación V1 vs V2:
# V1 - Lista simple, fallo total si hay errores supplies_v1 = client_v1.get_supplies() # List[SupplyData] or Exception # V2 - Respuesta robusta con información de errores response_v2 = client_v2.get_supplies() # SuppliesResponse supplies_v2 = response_v2.supplies # List[SupplyData] errors_v2 = response_v2.distributor_error # List[DistributorError]
Filtrar por distribuidor específico:
# Solo suministros de E-distribución response = client.get_supplies(distributor_code="2") if response.supplies: print("Suministros de E-distribución encontrados") elif response.distributor_error: for error in response.distributor_error: if error.distributorCode == "2": print(f"Error en E-distribución: {error.errorDescription}")
- Parámetros:
authorized_nif (Optional[str]) – NIF de una persona autorizada para consultar sus suministros. Si se especifica, se obtendrán los suministros de esa persona autorizada en lugar de los del usuario autenticado
distributor_code (Optional[str]) – Código del distribuidor para filtrar suministros. Solo acepta strings en V2 (ej: «2» para E-distribución)
- Devuelve:
Objeto
SuppliesResponseque contiene: -supplies: Lista de objetosSupplyDatavalidados -distributor_error: Lista de errores por distribuidor si los hay- Tipo del valor devuelto:
- Muestra:
AuthenticationError – Si las credenciales son inválidas o el token expira
APIError – Si la API devuelve un error HTTP crítico (400, 403, 404, 500, etc.)
DatadisError – Si ocurren errores de conexión o timeouts repetidos
ValidationError – Si los datos devueltos no pasan la validación Pydantic
Ver también
Documentación oficial:
GET /api-private/api/get-supplies-v2Comparar con
SimpleDatadisClientV1.get_supplies()para diferenciasPara obtener detalles del contrato:
get_contract_detail()
Added in version 2.0: Manejo de errores por distribuidor y respuesta estructurada
Nota
A diferencia de V1, esta operación puede tener éxito parcial: obtener suministros de algunos distribuidores aunque otros fallen.
- get_distributors(authorized_nif=None)[fuente]
Obtiene la lista de distribuidores con estructura mejorada (V2).
Consulta el endpoint
GET /api-private/api/get-distributors-with-supplies-v2para obtener información sobre las distribuidoras eléctricas donde el usuario tiene suministros activos. La V2 incluye manejo mejorado de errores y estructura de respuesta más detallada que proporciona mejor información diagnóstica.- Mejoras de V2 sobre V1:
Estructura de respuesta mejorada:
DistributorsResponsecon metadatosInformación de errores: Detalles específicos de fallos por distribuidor
Soporte para NIFs autorizados: Consultar distribuidores de terceros
Manejo robusto: Operación exitosa aunque algunos distribuidores fallen
- La respuesta incluye:
dist_existence_user: Objeto con
distributorCodes(lista de códigos)- distributor_error: Lista de errores específicos por distribuidor si los hay
Información detallada de qué distribuidores no respondieron correctamente
Códigos de error específicos y descripciones
Ejemplo
Obtener distribuidores del usuario autenticado:
with SimpleDatadisClientV2("12345678A", "password") as client: response = client.get_distributors() # Obtener códigos de distribuidores distributor_codes = response.dist_existence_user.get("distributorCodes", []) print(f"✓ Distribuidores encontrados: {len(distributor_codes)}") # Mapear códigos a nombres (referencia) distributor_names = { "1": "Viesgo", "2": "E-distribución", "3": "E-redes", "4": "ASEME", "5": "UFD", "6": "EOSA", "7": "CIDE", "8": "IDE" } for code in distributor_codes: name = distributor_names.get(code, f"Distribuidor {code}") print(f"- {name} (código: {code})") # Verificar errores if response.distributor_error: print(f"⚠️ Errores en {len(response.distributor_error)} distribuidores:") for error in response.distributor_error: print(f"- {error.distributorName}: {error.errorDescription}") # Usar códigos para consultas posteriores if distributor_codes: first_code = distributor_codes[0] supplies_response = client.get_supplies(distributor_code=first_code) print(f"Suministros en {distributor_names.get(first_code)}: {len(supplies_response.supplies)}")
Consultar distribuidores de una persona autorizada:
# Obtener distribuidores de un NIF autorizado response = client.get_distributors(authorized_nif="87654321B") auth_distributors = response.dist_existence_user.get("distributorCodes", []) print(f"Distribuidores del NIF autorizado: {auth_distributors}")
Comparación V1 vs V2:
# V1 - Lista simple de objetos DistributorData distributors_v1 = client_v1.get_distributors() # List[DistributorData] codes_v1 = [d.distributorCode for d in distributors_v1] # V2 - Respuesta estructurada con manejo de errores response_v2 = client_v2.get_distributors() # DistributorsResponse codes_v2 = response_v2.dist_existence_user.get("distributorCodes", []) errors_v2 = response_v2.distributor_error # Lista de errores si los hay
- Parámetros:
authorized_nif (Optional[str]) – NIF de una persona autorizada para consultar sus distribuidores. Si se especifica, se obtendrán los distribuidores donde esa persona tiene suministros en lugar del usuario autenticado
- Devuelve:
Objeto
DistributorsResponseque contiene: -dist_existence_user: Dict condistributorCodes(lista de códigos de distribuidor) -distributor_error: Lista de errores por distribuidor si los hay- Tipo del valor devuelto:
- Muestra:
AuthenticationError – Si las credenciales son inválidas o el token expira
APIError – Si la API devuelve un error HTTP crítico (400, 403, 404, 500, etc.)
DatadisError – Si ocurren errores de conexión o timeouts repetidos
ValidationError – Si los datos devueltos no pasan la validación Pydantic
Ver también
Documentación oficial:
GET /api-private/api/get-distributors-with-supplies-v2Comparar con
SimpleDatadisClientV1.get_distributors()para diferenciasLos códigos obtenidos se usan en
get_supplies(),get_consumption(), etc.
Added in version 2.0: Soporte para NIFs autorizados y estructura de respuesta mejorada
Nota
Solo se devuelven códigos de distribuidores donde el usuario (o NIF autorizado) tiene suministros activos. La lista puede estar vacía si no hay suministros.
- get_contract_detail(cups, distributor_code, authorized_nif=None)[fuente]
Obtiene los detalles del contrato eléctrico con manejo mejorado de errores (V2).
Consulta el endpoint
GET /api-private/api/get-contract-detail-v2para obtener información detallada del contrato asociado a un CUPS específico. La V2 incluye manejo mejorado de errores por distribuidor y soporte nativo para consultas con NIFs autorizados, proporcionando mayor robustez y flexibilidad.- Ventajas de V2 sobre V1:
Manejo de errores por distribuidor: Información detallada de fallos específicos
Soporte nativo para NIFs autorizados: Parámetro
authorized_nifintegradoRespuesta estructurada:
ContractResponsecon contratos y errores separadosMayor robustez: Operación exitosa aunque haya problemas con algunos distribuidores
- Información del contrato incluida (idéntica a V1):
Datos básicos: CUPS, distribuidor, comercializadora
Características técnicas: Tensión, potencia contratada, tipo de punto
Datos tarifarios: Tarifa de acceso, discriminación horaria
Ubicación: Provincia, municipio, código postal
Fechas: Inicio y fin de contrato, historial de cambios
Autoconsumo: Tipo, características y CAU si aplica
Control de potencia: ICP o Maxímetro
Ejemplo
Obtener detalles de contrato con manejo de errores V2:
with SimpleDatadisClientV2("12345678A", "password") as client: # Primero obtener suministros supplies_response = client.get_supplies() if supplies_response.supplies: supply = supplies_response.supplies[0] # Obtener detalles del contrato contract_response = client.get_contract_detail( cups=supply.cups, distributor_code=supply.distributorCode ) print(f"✓ Contratos obtenidos: {len(contract_response.contract)}") # Mostrar información de contratos for contract in contract_response.contract: print(f"CUPS: {contract.cups}") print(f"Distribuidor: {contract.distributor}") print(f"Potencia contratada: {contract.contractedPowerkW} kW") print(f"Tarifa de acceso: {contract.accessFare}") if contract.marketer: print(f"Comercializadora: {contract.marketer}") print("---") # Verificar errores específicos del distribuidor if contract_response.distributor_error: print("⚠️ Errores en la consulta:") for error in contract_response.distributor_error: print(f"- {error.distributorName}: {error.errorDescription}") else: print("✓ Consulta exitosa sin errores")
Consultar contrato con NIF autorizado:
# Obtener contrato de un suministro autorizado por tercero contract_response = client.get_contract_detail( cups="ES001234567890123456AB", distributor_code="2", authorized_nif="87654321B" # NIF que nos autorizó ) if contract_response.contract: contract = contract_response.contract[0] print(f"Contrato autorizado - Potencia: {contract.contractedPowerkW} kW") elif contract_response.distributor_error: for error in contract_response.distributor_error: print(f"Error en autorización: {error.errorDescription}")
Comparación V1 vs V2:
# V1 - Lista simple, fallo total si hay errores contracts_v1 = client_v1.get_contract_detail(cups, dist_code) # List[ContractData] # V2 - Respuesta robusta con manejo de errores response_v2 = client_v2.get_contract_detail(cups, dist_code) # ContractResponse contracts_v2 = response_v2.contract # List[ContractData] errors_v2 = response_v2.distributor_error # List[DistributorError]
- Parámetros:
cups (str) – Código CUPS del punto de suministro (22 caracteres alfanuméricos que identifican únicamente el punto de suministro eléctrico)
distributor_code (str) – Código del distribuidor eléctrico como string (ej: «2» para E-distribución). V2 requiere string
authorized_nif (Optional[str]) – NIF de la persona que autorizó la consulta de sus datos. Si se especifica, se consultará el contrato de esa persona en lugar del usuario autenticado
- Devuelve:
Objeto
ContractResponseque contiene: -contract: Lista de objetosContractDatavalidados -distributor_error: Lista de errores por distribuidor si los hay- Tipo del valor devuelto:
- Muestra:
AuthenticationError – Si las credenciales son inválidas o el token expira
APIError – Si la API devuelve un error HTTP crítico (400, 403, 404, 500, etc.)
DatadisError – Si ocurren errores de conexión o timeouts repetidos
ValidationError – Si los datos devueltos no pasan la validación Pydantic
ValueError – Si el CUPS no tiene el formato correcto (22 caracteres)
Ver también
Documentación oficial:
GET /api-private/api/get-contract-detail-v2Comparar con
SimpleDatadisClientV1.get_contract_detail()para diferenciasPara obtener la lista de CUPS:
get_supplies()Para datos de consumo del contrato:
get_consumption()
Added in version 2.0: Soporte nativo para
authorized_nify manejo de errores por distribuidorNota
El CUPS debe ser exactamente de 22 caracteres alfanuméricos. La V2 incluye validación mejorada y mensajes de error más descriptivos.
- get_consumption(cups, distributor_code, date_from, date_to, measurement_type=0, point_type=None, authorized_nif=None)[fuente]
Obtiene los datos de consumo eléctrico con validaciones mejoradas (V2).
Consulta el endpoint
GET /api-private/api/get-consumption-data-v2para obtener la curva de carga con manejo mejorado de errores y validaciones específicas V2. Incluye todas las funcionalidades de V1 más validaciones automáticas de rangos, manejo robusto de errores por distribuidor y soporte nativo para NIFs autorizados.- IMPORTANTE - Limitaciones idénticas a V1:
Solo fechas en formato mensual (YYYY/MM)
Datos cuarto-horarios limitados según tipo de punto
Conversión automática de fechas datetime/date
- Mejoras de V2 sobre V1:
Validación automática:
measurement_typeypoint_typese validan automáticamenteManejo de errores por distribuidor: Operación exitosa aunque algunos distribuidores fallen
Soporte nativo para NIFs autorizados: Parámetro
authorized_nifintegradoRespuesta estructurada:
ConsumptionResponsecon curva de tiempo y errores separadosMejor diagnóstico: Información detallada de problemas específicos por distribuidor
- Validaciones automáticas V2:
measurement_type: Se valida que esté en rango [0, 1]point_type: Se valida que esté en rango [1, 5] si se proporcionaCompatibilidad cuarto-horaria: Se verifica automáticamente según tipo de punto
Ejemplo
Obtener consumo con manejo de errores V2:
with SimpleDatadisClientV2("12345678A", "password") as client: supplies_response = client.get_supplies() if supplies_response.supplies: supply = supplies_response.supplies[0] # Consumo horario del año 2024 consumption_response = client.get_consumption( cups=supply.cups, distributor_code=supply.distributorCode, date_from="2024/01", date_to="2024/12", measurement_type=0 # Horarios - se valida automáticamente ) print(f"✓ Registros obtenidos: {len(consumption_response.time_curve)}") # Analizar consumo total_kwh = sum(c.consumptionKWh for c in consumption_response.time_curve if c.consumptionKWh) print(f"Consumo total 2024: {total_kwh:.2f} kWh") # Verificar errores específicos if consumption_response.distributor_error: print("⚠️ Errores encontrados:") for error in consumption_response.distributor_error: print(f"- {error.distributorName}: {error.errorDescription}") else: print("✓ Datos obtenidos sin errores")
Obtener datos cuarto-horarios con validación automática:
# V2 valida automáticamente la compatibilidad try: consumption_response = client.get_consumption( cups="ES001234567890123456AB", distributor_code="2", # E-distribución date_from="2024/06", date_to="2024/06", measurement_type=1, # Cuarto-horarios - se valida automáticamente point_type=3 # Tipo 3 - se valida que esté en [1-5] ) print(f"Datos cada 15 min: {len(consumption_response.time_curve)} registros") except ValidationError as e: print(f"Error de validación V2: {e}") except APIError as e: print(f"API rechazó la petición: {e}")
Consultar consumo con NIF autorizado:
consumption_response = client.get_consumption( cups="ES001234567890123456AB", distributor_code="2", date_from="2024/01", date_to="2024/03", authorized_nif="87654321B" # NIF que nos autorizó ) if consumption_response.time_curve: print(f"Datos autorizados obtenidos: {len(consumption_response.time_curve)}")
Comparación V1 vs V2:
# V1 - Lista simple, validación manual consumption_v1 = client_v1.get_consumption(cups, dist, date_from, date_to) # List[ConsumptionData] # V2 - Respuesta estructurada, validación automática response_v2 = client_v2.get_consumption(cups, dist, date_from, date_to) # ConsumptionResponse consumption_v2 = response_v2.time_curve # List[ConsumptionData] errors_v2 = response_v2.distributor_error # List[DistributorError]
- Parámetros:
cups (str) – Código CUPS del punto de suministro (22 caracteres alfanuméricos)
distributor_code (Union[str, int]) – Código del distribuidor eléctrico (acepta int o str, se convierte automáticamente)
date_from (Union[str, datetime, date]) – Fecha de inicio en formato YYYY/MM. También acepta objetos datetime/date que se convertirán automáticamente
date_to (Union[str, datetime, date]) – Fecha de fin en formato YYYY/MM. También acepta objetos datetime/date que se convertirán automáticamente
measurement_type (Union[int, float, str]) – Tipo de medición - 0: horarios (defecto), 1: cuarto-horarios. Se valida automáticamente en V2 que esté en rango [0, 1]
point_type (Optional[Union[int, float, str]]) – Tipo de punto de medida (1-5). Requerido para datos cuarto-horarios. Se valida automáticamente en V2 que esté en rango [1, 5]
authorized_nif (Optional[str]) – NIF de la persona que autorizó la consulta de sus datos. Si se especifica, se consultarán los datos de esa persona
- Devuelve:
Objeto
ConsumptionResponseque contiene: -time_curve: Lista de objetosConsumptionDatavalidados -distributor_error: Lista de errores por distribuidor si los hay- Tipo del valor devuelto:
- Muestra:
AuthenticationError – Si las credenciales son inválidas o el token expira
APIError – Si la API devuelve un error HTTP crítico (400, 403, 404, 500, etc.)
DatadisError – Si ocurren errores de conexión o timeouts repetidos
ValidationError – Si las fechas no están en formato mensual válido, los tipos de medición/punto están fuera de rango, o los datos no pasan la validación Pydantic mejorada de V2
ValueError – Si se solicitan datos cuarto-horarios para un tipo de punto no compatible
Ver también
Documentación oficial:
GET /api-private/api/get-consumption-data-v2Comparar con
SimpleDatadisClientV1.get_consumption()para diferenciasPara obtener CUPS:
get_supplies()Para datos de potencia máxima:
get_max_power()
Added in version 2.0: Validaciones automáticas de rangos, soporte para
authorized_nify manejo de errores por distribuidorAdvertencia
Los datos cuarto-horarios siguen teniendo las mismas limitaciones que en V1: solo disponibles para tipos de punto 1, 2, y 3 en E-distribución.
- get_max_power(cups, distributor_code, date_from, date_to, authorized_nif=None)[fuente]
Obtiene los datos de potencia máxima demandada con manejo mejorado de errores (V2).
Consulta el endpoint
GET /api-private/api/get-max-power-v2para obtener las potencias máximas registradas en cada período tarifario con manejo mejorado de errores por distribuidor y soporte nativo para NIFs autorizados. Mantiene toda la funcionalidad de V1 con mayor robustez y mejor información diagnóstica.- IMPORTANTE - Limitación idéntica a V1:
Solo acepta fechas en formato mensual (YYYY/MM). El SDK convierte automáticamente fechas datetime/date al formato requerido.
- Mejoras de V2 sobre V1:
Manejo de errores por distribuidor: Información detallada de fallos específicos
Soporte nativo para NIFs autorizados: Parámetro
authorized_nifintegradoRespuesta estructurada:
MaxPowerResponsecon potencias y errores separadosMayor robustez: Operación exitosa aunque algunos distribuidores fallen
Mejor diagnóstico: Códigos y descripciones de errores específicos
- Información de potencia máxima (idéntica a V1):
Potencia en Vatios (W): Demanda máxima registrada en cada período
Períodos tarifarios: VALLE, LLANO, PUNTA, P1-P6
Timestamp completo: Fecha y hora exacta del pico de potencia
Aplicaciones: Optimización de potencia contratada, identificación de picos
Ejemplo
Obtener potencias máximas con manejo de errores V2:
with SimpleDatadisClientV2("12345678A", "password") as client: supplies_response = client.get_supplies() if supplies_response.supplies: supply = supplies_response.supplies[0] # Potencias máximas de 2024 max_power_response = client.get_max_power( cups=supply.cups, distributor_code=supply.distributorCode, date_from="2024/01", date_to="2024/12" ) print(f"✓ Registros de potencia: {len(max_power_response.max_power)}") # Analizar potencias por período periods = {} for power in max_power_response.max_power: period = power.period if period not in periods: periods[period] = [] periods[period].append(power.maxPower) # Mostrar potencia máxima por período for period, powers in periods.items(): max_power_w = max(powers) max_power_kw = max_power_w / 1000 print(f"Período {period}: {max_power_kw:.2f} kW") # Verificar errores específicos if max_power_response.distributor_error: print("⚠️ Errores encontrados:") for error in max_power_response.distributor_error: print(f"- {error.distributorName}: {error.errorDescription}") else: print("✓ Datos obtenidos sin errores")
Consultar potencias con NIF autorizado:
max_power_response = client.get_max_power( cups="ES001234567890123456AB", distributor_code="2", date_from="2024/01", date_to="2024/12", authorized_nif="87654321B" # NIF que nos autorizó ) if max_power_response.max_power: # Encontrar la potencia máxima absoluta del período all_powers = [p.maxPower for p in max_power_response.max_power] peak_power_w = max(all_powers) peak_power_kw = peak_power_w / 1000 print(f"Pico máximo autorizado: {peak_power_kw:.2f} kW")
Identificar mes con mayor demanda:
# Agrupar por mes (idéntico a V1) monthly_max = {} for power in max_power_response.max_power: month = power.date[:7] # YYYY/MM if month not in monthly_max: monthly_max[month] = 0 monthly_max[month] = max(monthly_max[month], power.maxPower) if monthly_max: peak_month = max(monthly_max, key=monthly_max.get) peak_power_kw = monthly_max[peak_month] / 1000 print(f"Mayor demanda: {peak_power_kw:.2f} kW en {peak_month}")
Comparación V1 vs V2:
# V1 - Lista simple, fallo total si hay errores max_power_v1 = client_v1.get_max_power(cups, dist, date_from, date_to) # List[MaxPowerData] # V2 - Respuesta robusta con manejo de errores response_v2 = client_v2.get_max_power(cups, dist, date_from, date_to) # MaxPowerResponse max_power_v2 = response_v2.max_power # List[MaxPowerData] errors_v2 = response_v2.distributor_error # List[DistributorError]
- Parámetros:
cups (str) – Código CUPS del punto de suministro (22 caracteres alfanuméricos)
distributor_code (str) – Código del distribuidor eléctrico como string (ej: «2» para E-distribución). V2 requiere string
date_from (str) – Fecha de inicio en formato YYYY/MM. Se convierte automáticamente si se pasa como datetime/date
date_to (str) – Fecha de fin en formato YYYY/MM. Se convierte automáticamente si se pasa como datetime/date
authorized_nif (Optional[str]) – NIF de la persona que autorizó la consulta de sus datos. Si se especifica, se consultarán los datos de esa persona
- Devuelve:
Objeto
MaxPowerResponseque contiene: -max_power: Lista de objetosMaxPowerDatavalidados -distributor_error: Lista de errores por distribuidor si los hay- Tipo del valor devuelto:
- Muestra:
AuthenticationError – Si las credenciales son inválidas o el token expira
APIError – Si la API devuelve un error HTTP crítico (400, 403, 404, 500, etc.)
DatadisError – Si ocurren errores de conexión o timeouts repetidos
ValidationError – Si las fechas no están en formato mensual válido o los datos no pasan la validación Pydantic
ValueError – Si el CUPS no tiene el formato correcto
Ver también
Documentación oficial:
GET /api-private/api/get-max-power-v2Comparar con
SimpleDatadisClientV1.get_max_power()para diferenciasPara obtener CUPS:
get_supplies()Para datos de consumo:
get_consumption()Para detalles del contrato:
get_contract_detail()
Added in version 2.0: Soporte nativo para
authorized_nify manejo de errores por distribuidorNota
Las potencias se devuelven en Vatios (W) como en V1. Para obtener kilovatios (kW) divida el valor entre 1000.
- get_reactive_data(cups, distributor_code, date_from, date_to, authorized_nif=None)[fuente]
Obtiene datos de energía reactiva - Funcionalidad EXCLUSIVA de la API V2.
Consulta el endpoint
GET /api-private/api/get-reactive-data-v2para obtener información sobre la energía reactiva consumida por el punto de suministro. Esta funcionalidad es completamente nueva en V2 y no está disponible en V1.- ¿Qué es la energía reactiva?
La energía reactiva es la energía eléctrica que no realiza trabajo útil pero es necesaria para el funcionamiento de equipos inductivos (motores, transformadores, etc.). Se mide en kVARh y puede generar penalizaciones en la factura eléctrica.
- IMPORTANTE - Limitación de fechas:
Al igual que otros endpoints, solo acepta fechas en formato mensual (YYYY/MM). No se permiten fechas con días específicos.
- Información incluida en los datos reactivos:
Energía reactiva por período: P1, P2, P3, P4, P5, P6
Código y descripción: Información del tipo de medición
Períodos mensuales: Datos agregados por mes
CUPS asociado: Identificación del punto de suministro
- Períodos tarifarios de energía reactiva:
P1-P6: Períodos tarifarios según discriminación horaria
Valores en kVARh: Energía reactiva consumida en cada período
Agregación mensual: Datos totalizados por mes del período consultado
- Applications:
Análisis de eficiencia energética: Identificar equipos ineficientes
Optimización de costes: Reducir penalizaciones por energía reactiva
Gestión técnica: Mejorar el factor de potencia de la instalación
Auditorías energéticas: Análisis completo del comportamiento eléctrico
Ejemplo
Obtener datos de energía reactiva (exclusivo V2):
with SimpleDatadisClientV2("12345678A", "password") as client: supplies_response = client.get_supplies() if supplies_response.supplies: supply = supplies_response.supplies[0] # Energía reactiva del año 2024 reactive_data = client.get_reactive_data( cups=supply.cups, distributor_code=supply.distributorCode, date_from="2024/01", date_to="2024/12" ) print(f"✓ Datos de energía reactiva: {len(reactive_data)} registros") for data in reactive_data: print(f"CUPS: {data.reactive_energy.cups}") print(f"Código: {data.reactive_energy.code}") print(f"Descripción: {data.reactive_energy.code_desc}") # Analizar energía por períodos for energy_record in data.reactive_energy.energy: date = energy_record.date total_reactive = sum([ energy_record.energy_p1 or 0, energy_record.energy_p2 or 0, energy_record.energy_p3 or 0, energy_record.energy_p4 or 0, energy_record.energy_p5 or 0, energy_record.energy_p6 or 0 ]) print(f"{date}: {total_reactive:.2f} kVARh total") print("---")
Analizar penalizaciones potenciales:
# Buscar meses con alta energía reactiva for data in reactive_data: for energy_record in data.reactive_energy.energy: # Calcular energía reactiva total del mes monthly_reactive = sum([ energy_record.energy_p1 or 0, energy_record.energy_p2 or 0, energy_record.energy_p3 or 0, energy_record.energy_p4 or 0, energy_record.energy_p5 or 0, energy_record.energy_p6 or 0 ]) if monthly_reactive > 1000: # Umbral de ejemplo print(f"⚠️ {energy_record.date}: Alta energía reactiva ({monthly_reactive:.2f} kVARh)") print(" → Revisar equipos inductivos para optimizar factor de potencia")
Consultar con NIF autorizado:
reactive_data = client.get_reactive_data( cups="ES001234567890123456AB", distributor_code="2", date_from="2024/01", date_to="2024/06", authorized_nif="87654321B" ) if reactive_data: print("Datos de energía reactiva obtenidos con autorización")
¿Por qué no está disponible en V1?:
# V1 - Esta funcionalidad NO EXISTE # client_v1.get_reactive_data() # ← AttributeError: no existe # V2 - Funcionalidad completamente nueva reactive_data = client_v2.get_reactive_data(...) # ✓ Disponible
- Parámetros:
cups (str) – Código CUPS del punto de suministro (22 caracteres alfanuméricos)
distributor_code (str) – Código del distribuidor eléctrico como string (ej: «2» para E-distribución)
date_from (str) – Fecha de inicio en formato YYYY/MM
date_to (str) – Fecha de fin en formato YYYY/MM
authorized_nif (Optional[str]) – NIF de la persona que autorizó la consulta de sus datos. Si se especifica, se consultarán los datos de esa persona
- Devuelve:
Lista de objetos
ReactiveDatavalidados con Pydantic. Cada objeto contiene información detallada de energía reactiva por períodos tarifarios y fechas- Tipo del valor devuelto:
List[ReactiveData]
- Muestra:
AuthenticationError – Si las credenciales son inválidas o el token expira
APIError – Si la API devuelve un error HTTP (400, 403, 404, 500, etc.)
DatadisError – Si ocurren errores de conexión o timeouts repetidos
ValidationError – Si las fechas no están en formato mensual válido o los datos no pasan la validación Pydantic
ValueError – Si el CUPS no tiene el formato correcto
Ver también
Documentación oficial:
GET /api-private/api/get-reactive-data-v2Para datos de energía activa:
get_consumption()Para datos de potencia máxima:
get_max_power()Información sobre factor de potencia y energía reactiva en documentación técnica
Added in version 2.0: Funcionalidad completamente nueva, no disponible en V1
Nota
Esta funcionalidad puede no estar disponible para todos los tipos de punto o distribuidores. Consulte con su distribuidor la disponibilidad de estos datos.
Advertencia
La energía reactiva puede generar penalizaciones en la factura eléctrica. Use estos datos para optimizar el factor de potencia de su instalación.
- close()[fuente]
Cierra explícitamente la sesión HTTP y limpia los recursos del cliente V2.
- Realiza las siguientes operaciones de limpieza:
Cierra la sesión HTTP: Libera conexiones TCP activas
Invalida el token: Establece el token a None por seguridad
Libera recursos: Evita memory leaks en aplicaciones de larga duración
- ¿Cuándo llamar manualmente?
Normalmente NO es necesario llamar este método explícitamente cuando se usa el patrón
with(context manager), ya que__exit__lo llama automáticamente.- Sin embargo, puede ser útil en estos casos:
Aplicaciones de larga duración: Cuando el cliente se mantiene activo mucho tiempo
Gestión manual de recursos: Cuando no se usa context manager
Problemas de conectividad: Para forzar reconexión después de errores
Testing y debugging: Para controlar explícitamente el ciclo de vida
- V1 vs V2 - Diferencias en gestión de recursos:
V1: Gestión de recursos más simple, principalmente HTTP sessions
V2: Gestión más robusta con mejor manejo de tokens y sesiones
Ejemplo
Uso con context manager (recomendado):
# El método close() se llama automáticamente with SimpleDatadisClientV2("12345678A", "password") as client: supplies = client.get_supplies() # Al salir del bloque 'with', close() se ejecuta automáticamente print("✓ Sesión cerrada automáticamente")
Uso manual (casos especiales):
client = SimpleDatadisClientV2("12345678A", "password") try: supplies = client.get_supplies() consumption = client.get_consumption( cups=supplies.supplies[0].cups, distributor_code=supplies.supplies[0].distributorCode, date_from="2024/01", date_to="2024/03" ) # Procesar muchos datos... finally: client.close() # ← Llamada manual obligatoria print("✓ Recursos liberados manualmente")
Aplicación de larga duración:
class DatadisService: def __init__(self): self.client = None def connect(self): self.client = SimpleDatadisClientV2("12345678A", "password") def disconnect(self): if self.client: self.client.close() # ← Limpieza explícita self.client = None def get_recent_consumption(self): if not self.client: self.connect() return self.client.get_consumption(...)
Debugging y reconexión:
client = SimpleDatadisClientV2("12345678A", "password") # Primer intento try: supplies = client.get_supplies() except Exception as e: print(f"Error: {e}") client.close() # ← Limpiar estado # Reintentar con nueva sesión client = SimpleDatadisClientV2("12345678A", "password") supplies = client.get_supplies()
Nota
Este método es idempotente: se puede llamar múltiples veces sin efectos secundarios. Si la sesión ya está cerrada, no hace nada.
Advertencia
Después de llamar a
close(), el cliente queda inutilizable. Cualquier intento de hacer requests fallará. Cree una nueva instancia si necesita continuar usando la API.Ver también
__enter__()y__exit__()para el patrón context managerDocumentación de
requests.Session.close()para detalles técnicos
- __enter__()[fuente]
Método de entrada del context manager - Permite usar
withstatement.Implementa el protocolo de context manager de Python, permitiendo que el cliente se use con la declaración
withpara gestión automática de recursos.- ¿Qué hace este método?
Retorna la instancia actual: Permite acceso a todos los métodos del cliente
Inicialización implícita: La autenticación se realiza de forma lazy en el primer request
Establece el contexto: Prepara el cliente para uso dentro del bloque
with
- Ventajas del patrón context manager:
Gestión automática: Los recursos se liberan automáticamente al salir
Robustez ante errores: Cleanup garantizado incluso si ocurren excepciones
Código más limpio: No necesidad de llamadas manuales a
close()Mejores prácticas: Patrón estándar en Python para gestión de recursos
- V1 vs V2 - Context manager:
V1: Implementación básica del patrón
V2: Implementación mejorada con mejor gestión de excepciones y recursos
Ejemplo
Uso estándar con context manager:
# __enter__() se llama automáticamente aquí ↓ with SimpleDatadisClientV2("12345678A", "password") as client: # 'client' es lo que retorna __enter__() supplies = client.get_supplies() for supply in supplies.supplies: consumption = client.get_consumption( cups=supply.cups, distributor_code=supply.distributorCode, date_from="2024/01", date_to="2024/03" ) print(f"Consumo: {len(consumption)} registros") # __exit__() se llama automáticamente aquí ↑ print("✓ Recursos liberados automáticamente")
Context manager anidado:
with SimpleDatadisClientV2("12345678A", "password") as client: supplies = client.get_supplies() # Múltiples operaciones en el mismo contexto for supply in supplies.supplies[:3]: # Primeros 3 suministros distributors = client.get_distributors() contracts = client.get_contract_detail( cups=supply.cups, distributor_code=supply.distributorCode ) # V2 exclusive: Energía reactiva reactive_data = client.get_reactive_data( cups=supply.cups, distributor_code=supply.distributorCode, date_from="2024/01", date_to="2024/03" ) print(f"CUPS: {supply.cups}") print(f"- Contratos: {len(contracts)}") print(f"- Energía reactiva: {len(reactive_data)} registros")
Comparación con uso manual:
# ❌ Uso manual (más propenso a errores) client = SimpleDatadisClientV2("12345678A", "password") try: supplies = client.get_supplies() # ... hacer trabajo ... finally: client.close() # ← Fácil de olvidar # ✅ Context manager (recomendado) with SimpleDatadisClientV2("12345678A", "password") as client: supplies = client.get_supplies() # ... hacer trabajo ... # ← Cleanup automático garantizado
Manejo de excepciones transparente:
try: with SimpleDatadisClientV2("invalid_nif", "wrong_pass") as client: supplies = client.get_supplies() # ← AuthenticationError except AuthenticationError as e: print(f"Error de autenticación: {e}") # ← close() se ejecuta incluso con excepción
- Devuelve:
La propia instancia del cliente, permitiendo acceso a todos sus métodos
- Tipo del valor devuelto:
Nota
Este método NO realiza la autenticación. La autenticación se hace de forma lazy en el primer request que requiera token.
Ver también
__exit__()para el método de salida del context managerclose()para limpieza manual de recursosPEP 343 - Especificación del protocolo context manager
Added in version 2.0: Implementación mejorada del context manager con mejor gestión de recursos
- __exit__(exc_type, exc_val, exc_tb)[fuente]
Método de salida del context manager - Garantiza limpieza automática de recursos.
Se ejecuta automáticamente al salir del bloque
with, independientemente de si el código terminó normalmente o con una excepción. Garantiza que los recursos se liberen correctamente en todos los casos.- ¿Qué hace este método?
Llamada automática a close(): Libera sesiones HTTP y tokens
Cleanup garantizado: Se ejecuta incluso si ocurren excepciones
No suprime excepciones: Permite que las excepciones se propaguen normalmente
Gestión robusta: Implementación defensiva ante posibles errores
- Flujo de ejecución:
Entrada:
__enter__()retorna la instancia del clienteTrabajo: Se ejecuta el código dentro del bloque
withSalida:
__exit__()se llama automáticamente (incluso con excepciones)Cleanup:
close()libera todos los recursosPropagación: Las excepciones originales se mantienen
- V1 vs V2 - Gestión de excepciones:
V1: Implementación básica de cleanup
V2: Gestión más robusta, mejor logging de errores durante cleanup
Ejemplo
Flujo normal (sin excepciones):
with SimpleDatadisClientV2("12345678A", "password") as client: # __enter__() ejecutado ↑ supplies = client.get_supplies() print(f"Suministros: {len(supplies.supplies)}") # Código se ejecuta normalmente consumption = client.get_consumption( cups=supplies.supplies[0].cups, distributor_code=supplies.supplies[0].distributorCode, date_from="2024/01", date_to="2024/03" ) # __exit__(None, None, None) ejecutado aquí ↑ # Recursos liberados automáticamente print("✓ Operación completada, recursos liberados")
Flujo con excepción controlada:
try: with SimpleDatadisClientV2("12345678A", "wrong_password") as client: # __enter__() ejecutado ↑ supplies = client.get_supplies() # ← AuthenticationError except AuthenticationError as e: # __exit__(AuthenticationError, e, traceback) ya ejecutado ↑ print(f"Error de autenticación: {e}") print("✓ Recursos liberados automáticamente (con excepción)")
Flujo con excepción inesperada:
with SimpleDatadisClientV2("12345678A", "password") as client: # __enter__() ejecutado ↑ supplies = client.get_supplies() # Error inesperado en el código del usuario result = 1 / 0 # ← ZeroDivisionError # __exit__(ZeroDivisionError, exception, traceback) ejecutado ↑ # Recursos liberados automáticamente # ZeroDivisionError se propaga normalmente
Múltiples context managers:
# Cada context manager gestiona sus propios recursos with SimpleDatadisClientV2("user1_nif", "pass1") as client1: with SimpleDatadisClientV2("user2_nif", "pass2") as client2: # Ambos clientes activos supplies1 = client1.get_supplies() supplies2 = client2.get_supplies() # Comparar datos entre usuarios print(f"Usuario 1: {len(supplies1.supplies)} suministros") print(f"Usuario 2: {len(supplies2.supplies)} suministros") # client2.__exit__() ejecutado aquí # client1.__exit__() ejecutado aquí print("✓ Ambos clientes cerrados correctamente")
Debugging del context manager:
class DebuggingDatadisClient(SimpleDatadisClientV2): def __exit__(self, exc_type, exc_val, exc_tb): print(f"Saliendo del context manager...") if exc_type: print(f"- Con excepción: {exc_type.__name__}: {exc_val}") else: print("- Terminación normal") # Llamar al método padre para cleanup real super().__exit__(exc_type, exc_val, exc_tb) print("✓ Recursos liberados") with DebuggingDatadisClient("12345678A", "password") as client: supplies = client.get_supplies() # raise ValueError("Error de prueba") # ← Descomentar para probar
- Parámetros:
exc_type (Optional[Type[BaseException]]) – Tipo de excepción que causó la salida, o None si terminó normalmente
exc_val (Optional[BaseException]) – Instancia de la excepción que causó la salida, o None si terminó normalmente
exc_tb (Optional[TracebackType]) – Traceback de la excepción, o None si terminó normalmente
- Devuelve:
None (no suprime excepciones, permite propagación normal)
- Tipo del valor devuelto:
None
Nota
Este método NUNCA suprime excepciones (siempre retorna None/False). Las excepciones se propagan normalmente después del cleanup.
Advertencia
NO llame este método manualmente. Es invocado automáticamente por Python al salir del bloque
with. La llamada manual puede causar doble cleanup.Ver también
__enter__()para el método de entrada del context managerclose()para la implementación del cleanupPEP 343 - Especificación del protocolo context manager
Added in version 2.0: Implementación mejorada con mejor gestión de excepciones durante cleanup
Modelos de Datos
Datos de Consumo
- class datadis_python.models.consumption.ConsumptionData(*, cups, date, time, consumptionKWh, obtainMethod, surplusEnergyKWh=None, generationEnergyKWh=None, selfConsumptionEnergyKWh=None)[fuente]
Bases:
BaseModelModelo Pydantic para datos de consumo energético de Datadis.
Representa una medición de consumo eléctrico proveniente de los contadores inteligentes de las distribuidoras eléctricas españolas. Los datos incluyen tanto consumo tradicional como información de autoconsumo y generación para instalaciones con paneles solares u otras fuentes de energía renovable.
- Características principales:
Validación automática: Todos los campos se validan con Pydantic
Compatibilidad de alias: Soporta tanto nombres Python como nombres API
Datos de autoconsumo: Información completa para instalaciones fotovoltaicas
Granularidad temporal: Mediciones horarias o cuarto-horarias según disponibilidad
Métodos de obtención: Distingue entre mediciones reales y estimadas
- Tipos de mediciones soportadas:
Consumo tradicional: Energía consumida de la red eléctrica
Autoconsumo: Energía generada y consumida localmente (sin pasar por la red)
Excedentes/Vertidos: Energía generada y vendida a la red eléctrica
Generación total: Energía total producida por la instalación renovable
- Métodos de obtención de datos:
«Real»: Medición directa del contador inteligente
«Estimada»: Estimación basada en patrones históricos o interpolación
«Provisional»: Datos preliminares pendientes de validación final
Ejemplo
Uso básico del modelo:
from datadis_python.models.consumption import ConsumptionData # Datos típicos de consumo sin autoconsumo consumption_basic = ConsumptionData( cups="ES001234567890123456AB", date="2024/12/15", time="14:00", consumptionKWh=2.45, obtainMethod="Real" ) print(f"Consumo: {consumption_basic.consumption_kwh} kWh") print(f"Método: {consumption_basic.obtain_method}")
Datos de instalación con autoconsumo fotovoltaico:
# Instalación con paneles solares consumption_solar = ConsumptionData( cups="ES001234567890123456AB", date="2024/07/20", time="13:00", # Hora de máxima producción solar consumptionKWh=0.25, # Poca energía de la red obtainMethod="Real", surplusEnergyKWh=3.20, # Excedente vendido a la red generationEnergyKWh=5.80, # Generación total de paneles selfConsumptionEnergyKWh=2.60 # Autoconsumo directo ) # Verificar balance energético total_consumption = (consumption_solar.consumption_kwh + consumption_solar.self_consumption_energy_kwh) print(f"Consumo total real: {total_consumption} kWh") print(f"Excedente vendido: {consumption_solar.surplus_energy_kwh} kWh")
Validación automática con alias:
# Usando nombres de la API (camelCase) data_api = { "cups": "ES001234567890123456AB", "date": "2024/12/15", "time": "10:30", "consumptionKWh": 1.85, # Nombre API "obtainMethod": "Estimada" } consumption = ConsumptionData(**data_api) print(f"Consumo: {consumption.consumption_kwh}") # Acceso Python
- Parámetros:
cups (str) – Código CUPS del punto de suministro. Identificador único de 22 caracteres que identifica de forma inequívoca el punto de conexión a la red eléctrica
date (str) – Fecha de la medición en formato YYYY/MM/DD. Corresponde al día de la lectura del contador, en zona horaria española (CET/CEST)
time (str) – Hora de la medición en formato HH:MM (24h). Para mediciones horarias normalmente :00, para cuarto-horarias :00, :15, :30, :45
consumption_kwh (float) – Energía activa consumida desde la red eléctrica en kWh. Representa la energía que el usuario ha tomado de la red durante el período de medición
obtain_method (str) – Método de obtención de los datos. Valores posibles: «Real» (medición directa), «Estimada» (cálculo), «Provisional»
surplus_energy_kwh (Optional[float]) – Energía excedentaria vertida a la red en kWh. Solo aplica para instalaciones de autoconsumo con excedentes. Representa la energía generada localmente y vendida/cedida a la red
generation_energy_kwh (Optional[float]) – Energía total generada por la instalación renovable en kWh. Suma del autoconsumo directo más los excedentes vertidos. Solo aplica para instalaciones con generación propia
self_consumption_energy_kwh (Optional[float]) – Energía autoconsumida directamente en kWh. Energía generada localmente y consumida sin pasar por la red. Solo aplica para instalaciones de autoconsumo
consumptionKWh (float)
obtainMethod (str)
surplusEnergyKWh (float | None)
generationEnergyKWh (float | None)
selfConsumptionEnergyKWh (float | None)
- Muestra:
ValidationError – Si algún campo no cumple las validaciones de Pydantic (tipos incorrectos, valores nulos en campos obligatorios, etc.)
Nota
Para instalaciones con autoconsumo, se cumple la ecuación:
generation_energy_kwh = self_consumption_energy_kwh + surplus_energy_kwhVer también
ConsumptionResponse- Respuesta estructurada de la API V2SimpleDatadisClientV1.get_consumption()- Obtener datos V1SimpleDatadisClientV2.get_consumption()- Obtener datos V2
- model_config: ClassVar[ConfigDict] = {'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
Datos de Suministro
- class datadis_python.models.supply.SupplyData(*, address, cups, postalCode, province, municipality, distributor, validDateFrom, validDateTo=None, pointType, distributorCode)[fuente]
Bases:
BaseModelModelo Pydantic para datos de puntos de suministro eléctrico españoles.
Representa la información completa de un punto de suministro (CUPS) registrado en el sistema eléctrico español. Cada objeto contiene todos los datos técnicos, geográficos y contractuales necesarios para identificar y trabajar con un punto de conexión específico a la red eléctrica nacional.
- Información del punto de suministro:
Identificación única: Código CUPS de 20-22 caracteres
Ubicación física: Dirección completa, código postal, provincia y municipio
Información técnica: Tipo de punto de medida y clasificación
Datos del distribuidor: Empresa responsable de la red en la zona
Período contractual: Fechas de validez del contrato actual
- Tipos de punto de medida (point_type):
Tipo 1: Potencia contratada > 450 kW (grandes industrias)
Tipo 2: 50 kW < Potencia ≤ 450 kW (medianas industrias)
Tipo 3: 15 kW < Potencia ≤ 50 kW (pequeñas industrias, grandes comercios)
Tipo 4: 10 kW < Potencia ≤ 15 kW (pequeños comercios, grandes viviendas)
Tipo 5: Potencia ≤ 10 kW (viviendas domésticas típicas)
- Estructura del código CUPS:
Formato: ESXXXXXXXXXXXXXXXXXXX
Longitud: 20-22 caracteres alfanuméricos
Prefijo: Siempre «ES» para España
Identificador: Único a nivel nacional
Verificación: Dígitos de control incluidos
- Distribuidores por zona geográfica:
Viesgo (1): Cantabria, Asturias
E-distribución (2): Cobertura nacional, mayor distribuidor
E-redes (3): Galicia
ASEME (4): Melilla (ciudad autónoma)
UFD (5): Nacional, especialmente Cataluña y Madrid
EOSA (6): Aragón
CIDE (7): Ceuta (ciudad autónoma)
IDE (8): Islas Baleares
Ejemplo
Análisis básico de un punto de suministro:
from datadis_python.models.supply import SupplyData # Punto de suministro doméstico típico supply_home = SupplyData( address="Calle Mayor 123, 1º A", cups="ES001234567890123456AB", postalCode="28001", province="Madrid", municipality="Madrid", distributor="E-distribución", validDateFrom="2023/01/15", validDateTo=None, # Contrato activo pointType=5, # Doméstico distributorCode="2" # E-distribución ) print(f"CUPS: {supply_home.cups}") print(f"Ubicación: {supply_home.address}") print(f"Zona: {supply_home.municipality}, {supply_home.province}") print(f"Distribuidor: {supply_home.distributor}") # Determinar tipo de instalación point_types = { 1: "Gran industria (>450 kW)", 2: "Mediana industria (50-450 kW)", 3: "Pequeña industria (15-50 kW)", 4: "Comercio/gran vivienda (10-15 kW)", 5: "Vivienda doméstica (≤10 kW)" } print(f"Tipo: {point_types.get(supply_home.point_type, 'Desconocido')}")
Análisis de vigencia contractual:
from datetime import datetime def analyze_contract_validity(supply: SupplyData) -> str: \"\"\"Analiza el estado del contrato del suministro.\"\"\" if supply.valid_date_to is None: return "✅ Contrato activo (sin fecha de fin)" # Parsear fecha de fin (formato YYYY/MM/DD) try: end_date = datetime.strptime(supply.valid_date_to, "%Y/%m/%d") current_date = datetime.now() if end_date > current_date: days_remaining = (end_date - current_date).days return f"✅ Contrato activo ({days_remaining} días restantes)" else: return "❌ Contrato expirado" except ValueError: return "⚠️ Fecha de fin inválida" # Usar la función status = analyze_contract_validity(supply_home) print(f"Estado contractual: {status}")
Agrupación por distribuidor:
# Lista de suministros de un usuario supplies = [ SupplyData( address="Calle A", cups="ES0012...", postalCode="28001", province="Madrid", municipality="Madrid", distributor="E-distribución", validDateFrom="2023/01/01", pointType=5, distributorCode="2" ), SupplyData( address="Calle B", cups="ES0034...", postalCode="08001", province="Barcelona", municipality="Barcelona", distributor="UFD", validDateFrom="2022/06/15", pointType=4, distributorCode="5" ) ] # Agrupar por distribuidor by_distributor = {} for supply in supplies: dist_name = supply.distributor if dist_name not in by_distributor: by_distributor[dist_name] = [] by_distributor[dist_name].append(supply) print("Suministros por distribuidor:") for distributor, supply_list in by_distributor.items(): print(f"- {distributor}: {len(supply_list)} suministros") for supply in supply_list: print(f" * {supply.municipality} ({supply.cups[:8]}...)")
Validación de código CUPS:
def validate_cups_format(cups: str) -> bool: \"\"\"Valida formato básico del código CUPS español.\"\"\" # Verificaciones básicas if not cups.startswith("ES"): return False if len(cups) < 20 or len(cups) > 22: return False if not cups[2:].isalnum(): return False return True # Validar CUPS if validate_cups_format(supply_home.cups): print("✅ Formato CUPS válido") else: print("❌ Formato CUPS inválido")
Análisis geográfico:
# Identificar región por distribuidor regions = { "1": "Norte (Cantabria/Asturias)", "2": "Nacional (E-distribución)", "3": "Galicia", "4": "Melilla", "5": "Nacional (UFD)", "6": "Aragón", "7": "Ceuta", "8": "Baleares" } region = regions.get(supply_home.distributor_code, "Región desconocida") print(f"Región de distribución: {region}")
- Parámetros:
address (str) – Dirección física completa del punto de suministro. Incluye calle, número, piso/puerta si aplica. Corresponde a la ubicación real donde se encuentra la instalación eléctrica
cups (str) – Código CUPS (Código Único del Punto de Suministro). Identificador alfanumérico único de 20-22 caracteres que identifica inequívocamente el punto de conexión a la red eléctrica española
postal_code (str) – Código postal de la dirección del suministro. Código numérico de 5 dígitos que identifica la zona postal española
province (str) – Provincia española donde se ubica el punto de suministro. Corresponde a la división administrativa de primer nivel
municipality (str) – Municipio donde se encuentra el suministro eléctrico. División administrativa local dentro de la provincia
distributor (str) – Nombre comercial de la empresa distribuidora responsable de la red eléctrica en la zona geográfica del suministro
valid_date_from (str) – Fecha de inicio de validez del contrato actual en formato YYYY/MM/DD. Marca el comienzo del período contractual vigente
valid_date_to (Optional[str]) – Fecha de finalización del contrato en formato YYYY/MM/DD. None para contratos sin fecha de fin definida (más común)
point_type (int) – Tipo de punto de medida según clasificación técnica española. Entero del 1 al 5 que determina el tipo de instalación y medición
distributor_code (str) – Código numérico del distribuidor (1-8). Identificador único usado en las consultas API para el distribuidor específico
postalCode (str)
validDateFrom (str)
validDateTo (str | None)
pointType (int)
distributorCode (str)
- Muestra:
ValidationError – Si algún campo obligatorio falta o tiene formato incorrecto
Nota
El
point_typedetermina el tipo de contador y sistema de medición. Los tipos 1-3 suelen tener medición cuarto-horaria, los tipos 4-5 horaria.Truco
Use el
distributor_codepara consultas posteriores de consumo, contratos y otros datos específicos del punto de suministro.Ver también
SuppliesResponse- Respuesta estructurada de la API V2 que contiene estos datosSimpleDatadisClientV1.get_supplies()- Obtener puntos de suministro V1SimpleDatadisClientV2.get_supplies()- Obtener puntos de suministro V2Códigos CUPS oficiales en la documentación del sistema eléctrico español
Added in version 1.0: Modelo base para puntos de suministro del sistema eléctrico español
- model_config: ClassVar[ConfigDict] = {'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
Datos de Contrato
- class datadis_python.models.contract.ContractData(*, cups, distributor, marketer=None, tension, accessFare, province, municipality, postalCode, contractedPowerkW, timeDiscrimination=None, modePowerControl, startDate, endDate=None, codeFare, selfConsumptionTypeCode=None, selfConsumptionTypeDesc=None, section=None, subsection=None, partitionCoefficient=None, cau=None, installedCapacityKW=None, dateOwner=None, lastMarketerDate=None, maxPowerInstall=None)[fuente]
Bases:
BaseModelModelo Pydantic completo para datos contractuales de suministros eléctricos.
Representa la información contractual completa de un punto de suministro (CUPS), incluyendo datos técnicos, comerciales, tarifarios y de autoconsumo. Este modelo contiene toda la información relevante sobre el contrato eléctrico asociado a una instalación, desde datos básicos hasta configuraciones avanzadas de autoconsumo.
- Información contractual incluida:
Datos básicos: CUPS, distribuidor, comercializadora, ubicación
Información técnica: Tensión, potencias contratadas, control de potencia
Datos tarifarios: Tarifa de acceso, discriminación horaria, códigos CNMC
Autoconsumo: Tipo, configuración, coeficientes, CAU
Histórico: Períodos de propiedad, cambios de comercializadora
- Tipos de instalaciones soportadas:
Consumo tradicional: Sin generación propia
Autoconsumo individual: Instalación fotovoltaica privada
Autoconsumo colectivo: Instalaciones compartidas entre varios usuarios
Autoconsumo con excedentes: Venta de energía sobrante a la red
Autoconsumo sin excedentes: Generación solo para consumo propio
- Códigos de tarifa de acceso (CNMC):
2.0TD: Baja tensión ≤ 15 kW (doméstico típico)
3.0TD: Baja tensión > 15 kW ≤ 100 kW (comercios, pequeña industria)
6.1TD: Alta tensión 1-36 kV (gran industria)
6.2TD: Alta tensión 36-72.5 kV
6.3TD: Alta tensión 72.5-145 kV
6.4TD: Alta tensión ≥ 145 kV
Ejemplo
Contrato doméstico básico sin autoconsumo:
from datadis_python.models.contract import ContractData # Vivienda típica con tarifa 2.0TD contract_home = ContractData( cups="ES001234567890123456AB", distributor="E-distribución", marketer="Iberdrola", tension="BT", accessFare="2.0TD (Peaje de acceso 2.0TD)", province="Madrid", municipality="Madrid", postalCode="28001", contractedPowerkW=[5.75], # 5.75 kW contratados timeDiscrimination="DHA", # Discriminación horaria modePowerControl="ICP", # Interruptor Control Potencia startDate="2023/01/15", codeFare="2.0TD" ) print(f"Potencia contratada: {contract_home.contracted_power_kw[0]} kW") print(f"Tarifa: {contract_home.code_fare}")
Instalación con autoconsumo fotovoltaico:
from datadis_python.models.contract import ContractData, DateOwner # Casa con paneles solares y autoconsumo contract_solar = ContractData( cups="ES001234567890123456AB", distributor="UFD", marketer="Naturgy", tension="BT", accessFare="2.0TD con autoconsumo", province="Valencia", municipality="Valencia", postalCode="46001", contractedPowerkW=[4.60], modePowerControl="ICP", startDate="2022/03/01", codeFare="2.0TD", # Configuración de autoconsumo selfConsumptionTypeCode="41", selfConsumptionTypeDesc="Autoconsumo con excedentes acogido a compensación", installedCapacityKW=5.0, # 5 kW de paneles solares cau="ES00123456789", # Código de Autoconsumo Único dateOwner=[ DateOwner(startDate="2022/03/01", endDate="2024/12/31") ] ) print(f"Tipo autoconsumo: {contract_solar.self_consumption_type_desc}") print(f"Potencia instalada: {contract_solar.installed_capacity_kw} kW")
Autoconsumo colectivo con coeficiente de reparto:
# Instalación compartida en comunidad de vecinos contract_collective = ContractData( cups="ES001234567890123456AB", distributor="E-distribución", tension="BT", accessFare="2.0TD autoconsumo colectivo", province="Barcelona", municipality="Barcelona", postalCode="08001", contractedPowerkW=[3.45], codeFare="2.0TD", selfConsumptionTypeCode="43", selfConsumptionTypeDesc="Autoconsumo colectivo con excedentes", partitionCoefficient=0.15, # 15% del total generado cau="ES00987654321", installedCapacityKW=20.0, # Instalación total compartida startDate="2023/06/01" ) print(f"Coeficiente de reparto: {contract_collective.partition_coefficient}")
- Parámetros:
cups (str) – Código CUPS del punto de suministro. Identificador único nacional de 20-22 caracteres que identifica inequívocamente el punto de conexión
distributor (str) – Nombre de la empresa distribuidora responsable de la red en la zona geográfica del suministro
marketer (Optional[str]) – Empresa comercializadora que factura la energía. Solo visible si el usuario autenticado es propietario del CUPS
tension (str) – Nivel de tensión del suministro. Valores típicos: «BT» (Baja Tensión), «AT» (Alta Tensión), «MT» (Media Tensión)
access_fare (str) – Descripción completa de la tarifa de acceso aplicable. Incluye el código y descripción extendida
province (str) – Provincia donde se ubica físicamente el punto de suministro
municipality (str) – Municipio de ubicación del suministro eléctrico
postal_code (str) – Código postal de la dirección del punto de suministro
contracted_power_kw (List[float]) – Lista de potencias contratadas en kW por período tarifario. Para tarifas simples: un valor. Para discriminación horaria: múltiples valores
time_discrimination (Optional[str]) – Tipo de discriminación horaria aplicada. Valores típicos: «DHA» (Discriminación Horaria), «DHS» (Supervalle), None (tarifa simple)
mode_power_control (str) – Sistema de control de la potencia contratada. «ICP» (Interruptor Control Potencia) o «Maxímetro»
start_date (str) – Fecha de inicio de vigencia del contrato en formato YYYY/MM/DD
end_date (Optional[str]) – Fecha de finalización del contrato. None para contratos activos
code_fare (str) – Código oficial de la tarifa de acceso según clasificación CNMC. Define la estructura tarifaria aplicable
self_consumption_type_code (Optional[str]) – Código numérico del tipo de autoconsumo según RD 244/2019. Códigos 4X para diferentes modalidades de autoconsumo
self_consumption_type_desc (Optional[str]) – Descripción detallada del tipo de autoconsumo configurado. Especifica modalidad, excedentes y acogimiento a compensación
section (Optional[str]) – Clasificación de sección para autoconsumo según normativa vigente
subsection (Optional[str]) – Subclasificación específica dentro de la sección de autoconsumo
partition_coefficient (Optional[float]) – Coeficiente de reparto para autoconsumo colectivo. Porcentaje de la energía generada asignado a este CUPS (0.0-1.0)
cau (Optional[str]) – Código de Autoconsumo Único. Identificador oficial de la instalación de autoconsumo asignado por la administración competente
installed_capacity_kw (Optional[float]) – Potencia pico instalada de generación renovable en kW. Suma de toda la capacidad de generación asociada al autoconsumo
date_owner (Optional[List[DateOwner]]) – Lista de períodos durante los cuales el usuario autenticado ha sido propietario del punto de suministro
last_marketer_date (Optional[str]) – Fecha del último cambio de empresa comercializadora en formato YYYY/MM/DD
max_power_install (Optional[str]) – Potencia máxima de la instalación eléctrica en formato texto. Puede incluir información adicional sobre limitaciones técnicas
accessFare (str)
postalCode (str)
timeDiscrimination (str | None)
modePowerControl (str)
startDate (str)
endDate (str | None)
codeFare (str)
selfConsumptionTypeCode (str | None)
selfConsumptionTypeDesc (str | None)
partitionCoefficient (float | None)
installedCapacityKW (float | None)
lastMarketerDate (str | None)
maxPowerInstall (str | None)
- Muestra:
ValidationError – Si algún campo obligatorio está ausente o tiene formato incorrecto
Nota
Para autoconsumo colectivo, el
partition_coefficientdebe sumar 1.0 entre todos los participantes de la instalación compartida.Ver también
DateOwner- Modelo para períodos de propiedadContractResponse- Respuesta estructurada de la API V2SimpleDatadisClientV2.get_contract_detail()- Obtener datos contractualesRD 244/2019 para códigos de autoconsumo oficiales
Added in version 2.0: Soporte completo para autoconsumo y datos contractuales extendidos
- model_config: ClassVar[ConfigDict] = {'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
Datos de Distribuidor
- class datadis_python.models.distributor.DistributorData(*, distributorCodes)[fuente]
Bases:
BaseModelModelo Pydantic para datos de distribuidoras eléctricas españolas.
Representa la información de las empresas distribuidoras de energía eléctrica donde el usuario tiene puntos de suministro activos. Las distribuidoras son las empresas responsables del mantenimiento y operación de las redes eléctricas en España, y cada zona geográfica está asignada a una distribuidora específica.
- Sistema eléctrico español:
Las distribuidoras están reguladas por la CNMC (Comisión Nacional de los Mercados y la Competencia) y tienen asignadas zonas geográficas exclusivas donde son responsables de la red de distribución eléctrica.
- Distribuidoras principales en España:
Código «1»: Viesgo - Cantabria, Asturias
Código «2»: E-distribución (Endesa) - Nacional (mayor cobertura)
Código «3»: E-redes - Galicia
Código «4»: ASEME - Melilla
Código «5»: UFD (Naturgy) - Nacional, especialmente Cataluña, Madrid
Código «6»: EOSA - Aragón
Código «7»: CIDE - Ceuta
Código «8»: IDE (Redeia) - Islas Baleares
- Diferencia con comercializadoras:
Distribuidoras: Mantienen y operan la red física (cables, transformadores)
Comercializadoras: Venden la energía y emiten facturas al usuario final
Ejemplo
Uso básico del modelo:
from datadis_python.models.distributor import DistributorData # Datos típicos de respuesta V1 distributor_data = DistributorData( distributorCodes=["2", "5"] # E-distribución y UFD ) print("Distribuidoras donde tienes suministros:") for code in distributor_data.distributor_codes: distributor_name = { "1": "Viesgo", "2": "E-distribución (Endesa)", "3": "E-redes", "4": "ASEME", "5": "UFD (Naturgy)", "6": "EOSA", "7": "CIDE", "8": "IDE (Redeia)" }.get(code, f"Distribuidor {code}") print(f"- Código {code}: {distributor_name}")
Uso con clientes V1:
from datadis_python.client.v1 import SimpleDatadisClientV1 with SimpleDatadisClientV1("12345678A", "password") as client: # Obtener distribuidoras donde el usuario tiene suministros distributors = client.get_distributors() print(f"Distribuidoras encontradas: {len(distributors)}") for dist in distributors: print(f"Códigos: {dist.distributor_codes}") # Usar el primer código para consultas posteriores if distributors and distributors[0].distributor_codes: first_code = distributors[0].distributor_codes[0] supplies = client.get_supplies(distributor_code=first_code)
Filtrado por zona geográfica:
# Ejemplo: usuario con suministros en múltiples zonas multi_zone_data = DistributorData( distributorCodes=["2", "5", "8"] # Endesa, Naturgy, Baleares ) # Identificar regiones regions = { "2": "Península (Endesa)", "5": "Cataluña/Madrid (Naturgy)", "8": "Islas Baleares (IDE)" } for code in multi_zone_data.distributor_codes: print(f"Suministros en: {regions.get(code, 'Región desconocida')}")
- Parámetros:
- Muestra:
ValidationError – Si la lista está vacía o contiene valores no válidos
Nota
Este modelo representa la respuesta simplificada de la API V1. La API V2 utiliza modelos más complejos con información extendida de cada distribuidor.
Ver también
DistributorsResponse- Respuesta estructurada de la API V2SimpleDatadisClientV1.get_distributors()- Obtener distribuidoras V1SimpleDatadisClientV2.get_distributors()- Obtener distribuidoras V2Los códigos obtenidos se usan en métodos de consulta específicos
Added in version 1.0: Soporte para códigos de distribuidor en API V1
- model_config: ClassVar[ConfigDict] = {'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
Datos de Potencia Máxima
- class datadis_python.models.max_power.MaxPowerData(*, cups, date, time, maxPower, period)[fuente]
Bases:
BaseModelModelo Pydantic para datos de potencia máxima demandada en suministros eléctricos.
Representa el registro de potencia eléctrica máxima demandada en un punto de suministro durante un período determinado. Esta información es crucial para la facturación eléctrica, optimización de contratos y análisis de consumo, especialmente en instalaciones comerciales e industriales con control por maxímetro.
- Conceptos fundamentales:
Potencia máxima: Pico de demanda eléctrica registrado en un período
Control por maxímetro: Sistema que registra automáticamente los picos de potencia
Períodos tarifarios: Franjas horarias con diferentes precios de energía
Penalizaciones: Excesos sobre la potencia contratada pueden generar recargos
- Sistemas de control de potencia en España:
ICP (Interruptor Control Potencia): Corta el suministro si se excede la potencia
Maxímetro: Registra los picos pero permite el consumo (con posible recargo)
- Períodos tarifarios comunes:
PUNTA: Horas de mayor demanda nacional (18:00-22:00 invierno)
LLANO: Horas intermedias de demanda
VALLE: Horas de menor demanda (01:00-08:00)
P1-P6: Períodos numerados según discriminación horaria específica
Ejemplo
Análisis de potencia máxima doméstica:
from datadis_python.models.max_power import MaxPowerData # Pico de potencia en hora punta max_power_home = MaxPowerData( cups="ES001234567890123456AB", date="2024/12/15", time="19:30", # Hora punta de invierno maxPower=4250.0, # 4.25 kW period="PUNTA" ) print(f"Potencia máxima: {max_power_home.max_power / 1000:.2f} kW") print(f"Momento pico: {max_power_home.date} a las {max_power_home.time}") print(f"Período tarifario: {max_power_home.period}") # Verificar si excede potencia contratada potencia_contratada = 5750 # 5.75 kW en W if max_power_home.max_power > potencia_contratada: exceso = max_power_home.max_power - potencia_contratada print(f"⚠️ Exceso: {exceso:.0f} W sobre lo contratado") else: print("✅ Dentro de la potencia contratada")
Instalación comercial con múltiples períodos:
# Ejemplo de pequeño comercio power_records = [ MaxPowerData( cups="ES009876543210987654AB", date="2024/11/20", time="09:15", maxPower=12500.0, # 12.5 kW period="LLANO" ), MaxPowerData( cups="ES009876543210987654AB", date="2024/11/20", time="20:45", maxPower=15200.0, # 15.2 kW period="PUNTA" ) ] # Analizar picos por período for record in power_records: kw_power = record.max_power / 1000 print(f"Período {record.period}: {kw_power:.1f} kW a las {record.time}")
Optimización de contrato basada en datos históricos:
from datadis_python.client.v2 import SimpleDatadisClientV2 with SimpleDatadisClientV2("12345678A", "password") as client: # Obtener datos de potencia máxima del último año max_power_response = client.get_max_power( cups="ES001234567890123456AB", distributor_code="2", date_from="2024/01", date_to="2024/12" ) # Analizar patrones para optimizar contrato monthly_peaks = {} for power_data in max_power_response.max_power_data: month = power_data.date[:7] # YYYY/MM current_peak = monthly_peaks.get(month, 0) monthly_peaks[month] = max(current_peak, power_data.max_power) avg_peak = sum(monthly_peaks.values()) / len(monthly_peaks) print(f"Potencia promedio máxima: {avg_peak/1000:.2f} kW")
- Parámetros:
cups (str) – Código CUPS del punto de suministro. Identificador único del punto de conexión donde se registró la potencia máxima
date (str) – Fecha en que se registró la potencia máxima en formato YYYY/MM/DD. Corresponde al día específico en que ocurrió el pico de demanda
time (str) – Hora exacta del pico de potencia en formato HH:MM (24h). Momento preciso en que se registró la demanda máxima del período
max_power (float) – Potencia máxima demandada expresada en vatios (W). Representa el pico de consumo eléctrico registrado en el momento especificado
period (str) – Período tarifario en el que ocurrió el pico. Valores típicos: «PUNTA», «LLANO», «VALLE» o códigos numéricos «P1»-«P6» según discriminación horaria
maxPower (float)
- Muestra:
ValidationError – Si algún campo obligatorio falta o tiene formato incorrecto
Nota
La potencia se expresa en vatios (W). Para convertir a kilovatios:
max_power / 1000Truco
Para instalaciones con ICP, los picos registrados normalmente no excederán la potencia contratada ya que el interruptor cortaría el suministro.
Ver también
MaxPowerResponse- Respuesta estructurada de la API V2SimpleDatadisClientV1.get_max_power()- Obtener datos V1SimpleDatadisClientV2.get_max_power()- Obtener datos V2ContractData- Información sobre potencias contratadas
Added in version 1.0: Soporte para datos de potencia máxima de contadores inteligentes
- model_config: ClassVar[ConfigDict] = {'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
Datos de Energía Reactiva
- class datadis_python.models.reactive.ReactiveData(*, reactiveEnergy)[fuente]
Bases:
BaseModelModelo Pydantic simplificado para respuesta de energía reactiva.
Wrapper o contenedor simple para datos de energía reactiva que encapsula la información principal en un objeto ReactiveEnergyData. Este modelo representa la estructura de respuesta básica de algunos endpoints de energía reactiva, proporcionando acceso directo a los datos principales.
- Casos de uso:
Respuestas simples: Cuando solo se necesitan los datos principales
Compatibilidad: Para mantener consistencia con otras APIs
Encapsulación: Estructura que puede extenderse en futuras versiones
Ejemplo
Uso básico del modelo wrapper:
from datadis_python.models.reactive import ReactiveData, ReactiveEnergyData # Datos encapsulados en el wrapper reactive_wrapper = ReactiveData( reactiveEnergy=ReactiveEnergyData( cups="ES001234567890123456AB", energy=[], # Lista de períodos code=None, code_desc=None ) ) # Acceso a los datos principales main_data = reactive_wrapper.reactive_energy print(f"CUPS: {main_data.cups}") print(f"Períodos disponibles: {len(main_data.energy)}")
Procesamiento directo:
# Trabajar directamente con los datos encapsulados if reactive_wrapper.reactive_energy.code is None: # Procesar datos de energía reactiva for period in reactive_wrapper.reactive_energy.energy: print(f"Mes {period.date}: datos disponibles") else: print(f"Error: {reactive_wrapper.reactive_energy.code_desc}")
- Parámetros:
reactive_energy (ReactiveEnergyData) – Objeto ReactiveEnergyData que contiene toda la información de energía reactiva para el punto de suministro consultado
reactiveEnergy (ReactiveEnergyData)
- Muestra:
ValidationError – Si el objeto ReactiveEnergyData no es válido
Nota
Este modelo es principalmente un wrapper. Para acceso a datos detallados, utilice directamente el atributo
reactive_energy.Ver también
ReactiveEnergyData- Modelo principal con los datos detalladosReactiveResponse- Versión extendida con manejo de errores por distribuidor
- reactive_energy: ReactiveEnergyData
- model_config: ClassVar[ConfigDict] = {'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
Respuestas de la API
Excepciones
Excepciones personalizadas para el SDK de Datadis.
- author:
TacoronteRiveroCristian
- exception datadis_python.exceptions.DatadisError[fuente]
Bases:
ExceptionBase exception for Datadis SDK.
Esta es la excepción base de la cual heredan todas las demás excepciones del SDK.
- exception datadis_python.exceptions.AuthenticationError[fuente]
Bases:
DatadisErrorAuthentication related errors.
Se lanza cuando hay problemas con la autenticación del usuario.
- exception datadis_python.exceptions.APIError(message, status_code=None)[fuente]
Bases:
DatadisErrorAPI response errors.
Se lanza cuando la API de Datadis devuelve errores HTTP.
- exception datadis_python.exceptions.ValidationError[fuente]
Bases:
DatadisErrorParameter validation errors.
Se lanza cuando los parámetros proporcionados no son válidos.
Utilidades
Validadores
Validadores especializados para parámetros de la API de Datadis.
Este módulo proporciona funciones de validación específicas para los diferentes tipos de parámetros que acepta la API de Datadis. Cada validador implementa las reglas de negocio y restricciones técnicas específicas de la plataforma.
Los validadores están diseñados para detectar y prevenir errores comunes antes de realizar peticiones a la API, proporcionando mensajes de error claros y específicos que guían al usuario hacia la solución correcta.
- Categorías de validación:
Identificadores: CUPS, códigos de distribuidor
Fechas y rangos: Validación temporal con restricciones de Datadis
Tipos de datos: Tipos de medida, tipos de punto
Formatos: Patrones específicos requeridos por la API
- Características principales:
Validación temprana: Detecta errores antes de peticiones HTTP
Mensajes específicos: Guía clara sobre cómo corregir problemas
Reglas de negocio: Implementa restricciones específicas de Datadis
Robustez: Manejo de casos edge y entradas malformadas
- Restricciones específicas de Datadis:
Rango temporal: Solo últimos 2 años de datos disponibles
Formato de fechas: Principalmente YYYY/MM (mensual)
Códigos limitados: Solo 8 distribuidores oficiales válidos
CUPS españoles: Formato específico ES + 20-22 caracteres
Ejemplo
Validaciones independientes:
from datadis_python.utils.validators import (
validate_cups,
validate_distributor_code,
validate_date_range
)
# Validar CUPS
valid_cups = validate_cups("ES0031607515707001RC0F")
# Validar distribuidor
valid_code = validate_distributor_code("2") # E-distribución
# Validar rango de fechas
from_date, to_date = validate_date_range("2024/01", "2024/12", "monthly")
Integración con conversores:
# Los validadores se llaman automáticamente desde los conversores
from datadis_python.utils.type_converters import (
convert_cups_parameter,
convert_distributor_code_parameter
)
# Conversión + validación automática
cups = convert_cups_parameter("ES0031607515707001RC0F")
code = convert_distributor_code_parameter(2) # int → "2" + validación
Nota
Todos los validadores lanzan ValidationError con mensajes descriptivos
cuando encuentran problemas. Esto permite manejo de errores consistente
en todo el SDK.
Advertencia
Las validaciones están sincronizadas con las limitaciones actuales de la API de Datadis (2025). Si la API cambia sus restricciones, estos validadores necesitarán actualizarse.
- author:
TacoronteRiveroCristian
- datadis_python.utils.validators.validate_cups(cups)[fuente]
Valida el formato del código CUPS (Código Universal del Punto de Suministro).
Los códigos CUPS son identificadores únicos obligatorios para todos los puntos de suministro eléctrico en España. Esta función valida que el código cumple con el formato oficial establecido por el sistema eléctrico español.
- Especificaciones del formato CUPS:
Prefijo obligatorio: «ES» (código ISO del país)
Longitud variable: 20-22 caracteres alfanuméricos después del prefijo
Total: 22-24 caracteres (incluyendo «ES»)
Caracteres válidos: Solo letras mayúsculas (A-Z) y números (0-9)
Sin separadores: No se permiten espacios, guiones u otros caracteres
- Ejemplos de códigos CUPS reales:
Formato 20:
ES0031607515707001RC0F(22 caracteres totales)Formato 22:
ES1234567890123456789012(24 caracteres totales)
- Proceso de validación:
Verificación de vacío: El CUPS no puede estar vacío o ser None
Normalización: Conversión a mayúsculas y eliminación de espacios
Validación de patrón: Verificación contra regex oficial
Verificación de longitud: Confirmar longitud dentro del rango válido
- Parámetros:
cups (str) – Código CUPS a validar. Debe ser un string con formato válido
- Devuelve:
Código CUPS validado, normalizado (mayúsculas, sin espacios)
- Tipo del valor devuelto:
- Muestra:
ValidationError – Si el CUPS no cumple con el formato esperado
Ejemplo
Códigos CUPS válidos:
# Formato estándar (20 caracteres después de ES) validate_cups("ES0031607515707001RC0F") # → "ES0031607515707001RC0F" # Formato extendido (22 caracteres después de ES) validate_cups("ES1234567890123456789012") # → "ES1234567890123456789012" # Con espacios (se limpian automáticamente) validate_cups(" ES0031607515707001RC0F ") # → "ES0031607515707001RC0F" # Minúsculas (se convierten automáticamente) validate_cups("es0031607515707001rc0f") # → "ES0031607515707001RC0F"
Casos que fallan (errores esperados):
# ❌ Código vacío validate_cups("") # → ValidationError: "CUPS no puede estar vacío" # ❌ Sin prefijo ES validate_cups("0031607515707001RC0F") # → ValidationError: "Formato CUPS inválido. Debe ser: ES + 20-22 caracteres alfanuméricos" # ❌ Demasiado corto validate_cups("ES123456") # → ValidationError: "Formato CUPS inválido..." # ❌ Caracteres inválidos validate_cups("ES003160751570700@RC0F") # → ValidationError: "Formato CUPS inválido..." # ❌ Prefijo incorrecto validate_cups("FR0031607515707001RC0F") # → ValidationError: "Formato CUPS inválido..."
Uso en validación de entrada:
def get_consumption(self, cups: str, ...): # Validar antes de usar en petición validated_cups = validate_cups(cups) # validated_cups está garantizado como válido params = {"cups": validated_cups}
Nota
Esta función valida únicamente el formato del CUPS, no verifica que el código exista realmente o esté asociado a un usuario específico. Esa validación la realiza el servidor de Datadis.
- Technical Details:
Regex utilizado:
^ES[A-Z0-9]{20,22}$Normalización:
.upper().strip()automáticaPerformance: Validación muy rápida usando regex compilado
Memoria: No guarda estado entre llamadas
Referencias
CNE (Comisión Nacional de Energía): Especificaciones oficiales CUPS
BOE: Normativa sobre identificación de puntos de suministro
Datadis: Documentación técnica de la API
Ver también
convert_cups_parameter()para conversión automática con validaciónDocumentación oficial sobre códigos CUPS en España
Added in version 1.0: Validación básica de formato CUPS
Distinto en la versión 2.0: Mejorado soporte para formatos CUPS extendidos (22 caracteres)
- datadis_python.utils.validators.validate_date_range(date_from, date_to, format_type='monthly')[fuente]
Valida un rango de fechas según las restricciones específicas de Datadis.
Esta función implementa todas las reglas de validación temporal específicas de la API de Datadis, incluyendo formatos aceptados, rangos permitidos y limitaciones de acceso a datos históricos.
- Restricciones de Datadis implementadas:
Rango temporal: Solo últimos 2 años desde la fecha actual
No fechas futuras: Las fechas no pueden ser posteriores al día actual
Orden lógico:
date_fromdebe ser anterior o igual adate_toFormatos específicos: YYYY/MM para mensual, YYYY/MM/DD para diario
- Formatos soportados:
Mensual: «YYYY/MM» (ej: «2024/01») - Recomendado para Datadis
Diario: «YYYY/MM/DD» (ej: «2024/01/15») - Limitado en algunos endpoints
- Validaciones aplicadas:
Formato de entrada: Verificación contra patrones regex específicos
Parseabilidad: Verificación de fechas válidas (no 30 de febrero)
Orden lógico: date_from ≤ date_to
Límite histórico: No más de 2 años hacia atrás
Límite futuro: No fechas posteriores a hoy
- Parámetros:
- Devuelve:
Tupla con las fechas validadas
(date_from, date_to)- Tipo del valor devuelto:
- Muestra:
ValidationError – Si cualquier validación falla
Ejemplo
Validaciones exitosas:
# Rango mensual válido validate_date_range("2024/01", "2024/12", "monthly") # → ("2024/01", "2024/12") # Rango diario válido validate_date_range("2024/01/01", "2024/01/31", "daily") # → ("2024/01/01", "2024/01/31") # Rango de un mes validate_date_range("2024/06", "2024/06", "monthly") # → ("2024/06", "2024/06")
Casos que fallan (errores esperados):
# ❌ Formato incorrecto validate_date_range("2024-01", "2024-12", "monthly") # → ValidationError: "Formato de fecha_desde inválido: 2024-01. Use 2024/01" # ❌ Rango invertido validate_date_range("2024/12", "2024/01", "monthly") # → ValidationError: "fecha_desde no puede ser posterior a fecha_hasta" # ❌ Demasiado antigua (más de 2 años) validate_date_range("2020/01", "2020/12", "monthly") # → ValidationError: "fecha_desde no puede ser anterior a hace 2 años" # ❌ Fecha futura validate_date_range("2030/01", "2030/12", "monthly") # → ValidationError: "fecha_hasta no puede ser futura" # ❌ Fecha inválida validate_date_range("2024/02/30", "2024/02/30", "daily") # → ValidationError: "Fecha inválida: day is out of range for month"
Uso típico en métodos del SDK:
def get_consumption(self, date_from, date_to): # Validación robusta antes de petición API validated_from, validated_to = validate_date_range( date_from, date_to, "monthly" ) # Las fechas están garantizadas como válidas params = {"dateFrom": validated_from, "dateTo": validated_to}
Nota
El límite de 2 años se basa en las limitaciones actuales de Datadis para acceso a datos históricos. Este límite puede cambiar en el futuro según las políticas de la plataforma.
- Performance:
La validación incluye cálculos de fecha para verificar límites temporales. Para uso intensivo, considere cachear los límites calculados.
- Technical Details:
Regex patterns: Específicos para cada formato (daily/monthly)
Parsing: Usa
datetime.strptime()para validación completaLímites dinámicos: Calculados en tiempo real basados en
datetime.now()Timezone: Usa timezone local del sistema
Ver también
convert_date_range_to_api_format()para conversión + validaciónconvert_date_to_api_format()para fechas individualesDocumentación de Datadis sobre disponibilidad de datos históricos
Added in version 1.0: Validación básica de rangos de fechas
Distinto en la versión 2.0: Añadidas validaciones específicas para limitaciones de Datadis
- datadis_python.utils.validators.validate_distributor_code(distributor_code)[fuente]
Valida códigos de distribuidor eléctrico españoles oficiales.
- Parámetros:
distributor_code (str) – Código de distribuidor a validar
- Devuelve:
Código de distribuidor validado
- Tipo del valor devuelto:
- Muestra:
ValidationError – Si el código no corresponde a un distribuidor válido
Ejemplo
Códigos válidos:
# Distribuidores nacionales principales validate_distributor_code("2") # → "2" (E-distribución/Endesa) validate_distributor_code("5") # → "5" (UFD/Naturgy) # Distribuidores regionales validate_distributor_code("1") # → "1" (Viesgo - Norte) validate_distributor_code("3") # → "3" (E-redes - Galicia) validate_distributor_code("6") # → "6" (EOSA - Aragón) # Distribuidores insulares/locales validate_distributor_code("8") # → "8" (IDE - Baleares) validate_distributor_code("4") # → "4" (ASEME - Melilla) validate_distributor_code("7") # → "7" (CIDE - Ceuta)
Casos que fallan (errores esperados):
# ❌ Código inexistente validate_distributor_code("9") # → ValidationError: "Código de distribuidor inválido: 9. Válidos: 1, 2, 3, 4, 5, 6, 7, 8" # ❌ Código múltiple validate_distributor_code("12") # → ValidationError: "Código de distribuidor inválido: 12..." # ❌ Código negativo validate_distributor_code("-1") # → ValidationError: "Código de distribuidor inválido: -1..."
Uso con constantes descriptivas:
from datadis_python.utils.constants import DISTRIBUTOR_CODES # Usar nombres descriptivos en lugar de números endesa_code = DISTRIBUTOR_CODES["E_DISTRIBUCION"] # "2" validated = validate_distributor_code(endesa_code) # "2" # Iterar sobre todos los distribuidores válidos for name, code in DISTRIBUTOR_CODES.items(): validated_code = validate_distributor_code(code) print(f"{name}: {validated_code}")
Nota
Los códigos de distribuidor son siempre strings en la API de Datadis, aunque representen números. Use
convert_distributor_code_parameter()si necesita conversión automática desde enteros.- Technical Details:
Lista estática: Los 8 códigos están hardcodeados (no cambian frecuentemente)
Validación rápida: Lookup en lista pequeña, muy eficiente
Case sensitive: Los códigos deben ser exactamente como se especifican
No conversión: Esta función no convierte tipos, solo valida
Referencias
CNE: Comisión Nacional de Energía - Listado oficial de distribuidores
CNMC: Comisión Nacional de los Mercados y la Competencia
Datadis: Documentación oficial de códigos soportados
Ver también
convert_distributor_code_parameter()para conversión automática int→strdatadis_python.utils.constants.DISTRIBUTOR_CODESpara mapeo de nombresDocumentación oficial de distribuidores eléctricos en España
Added in version 1.0: Validación de códigos de distribuidor
Distinto en la versión 2.0: Actualizada lista con información geográfica detallada
- datadis_python.utils.validators.validate_measurement_type(measurement_type)[fuente]
Valida y normaliza tipos de medida eléctrica para la API de Datadis.
Los tipos de medida distinguen entre consumo de energía (tomada de la red) y generación de energía (inyectada a la red). Esta distinción es importante para usuarios con instalaciones de autoconsumo que pueden tanto consumir como generar electricidad.
- Tipos de medida oficiales:
0 - CONSUMO: Energía consumida desde la red eléctrica (por defecto)
1 - GENERACIÓN: Energía generada e inyectada a la red
- Casos de uso:
Consumo doméstico: Siempre tipo 0 (viviendas sin generación)
Autoconsumo con inyección: Tipo 0 para consumo, tipo 1 para excedentes
Plantas de generación: Principalmente tipo 1
Instalaciones mixtas: Ambos tipos según el flujo de energía
- Manejo de valores por defecto:
None: Se convierte automáticamente a 0 (consumo)
Valor explícito: Se valida que esté en el rango permitido
- Parámetros:
measurement_type (Optional[int]) –
Tipo de medida a validar:
0oNone: Consumo (energía tomada de la red)1: Generación (energía inyectada a la red)
- Devuelve:
Tipo de medida validado (siempre 0 o 1)
- Tipo del valor devuelto:
- Muestra:
ValidationError – Si el tipo no está en el rango válido (0-1)
Ejemplo
Valores válidos:
# Consumo explícito validate_measurement_type(0) # → 0 (consumo) # Generación explícita validate_measurement_type(1) # → 1 (generación) # Valor por defecto (None → consumo) validate_measurement_type(None) # → 0 (consumo por defecto)
Casos que fallan (errores esperados):
# ❌ Tipo inválido validate_measurement_type(2) # → ValidationError: "measurement_type debe ser 0 (consumo) o 1 (generación)" # ❌ Tipo negativo validate_measurement_type(-1) # → ValidationError: "measurement_type debe ser 0 (consumo) o 1 (generación)"
Uso típico en consultas:
# Consulta de consumo doméstico (por defecto) consumption_data = client.get_consumption( cups="ES001234567890123456AB", distributor_code="2", date_from="2024/01", date_to="2024/12", measurement_type=validate_measurement_type(None) # → 0 ) # Consulta de generación solar generation_data = client.get_consumption( cups="ES001234567890123456AB", distributor_code="2", date_from="2024/01", date_to="2024/12", measurement_type=validate_measurement_type(1) # → 1 )
Uso con constantes:
from datadis_python.utils.constants import MEASUREMENT_TYPES # Usar constantes descriptivas consumption_type = validate_measurement_type(MEASUREMENT_TYPES["CONSUMPTION"]) # 0 generation_type = validate_measurement_type(MEASUREMENT_TYPES["GENERATION"]) # 1
Nota
La mayoría de usuarios domésticos solo necesitarán el tipo 0 (consumo). El tipo 1 (generación) es relevante principalmente para instalaciones con paneles solares u otras fuentes de generación distribuida.
- Technical Details:
Rango válido: Solo 0 y 1 según especificación de Datadis
Default seguro: None se convierte a 0 (caso más común)
Validación estricta: No se permiten otros valores enteros
Performance: Validación muy rápida (comparación simple)
Ver también
datadis_python.utils.constants.MEASUREMENT_TYPESpara constantes descriptivasDocumentación de Datadis sobre tipos de medida
Normativa española sobre autoconsumo y balance neto
Added in version 1.0: Validación de tipos de medida
Distinto en la versión 2.0: Mejorada documentación con casos de uso específicos
- datadis_python.utils.validators.validate_point_type(point_type)[fuente]
Valida y normaliza tipos de punto de medida según normativa eléctrica española.
- Parámetros:
point_type (Optional[int]) – Tipo de punto, None para usar por defecto (1)
- Devuelve:
Tipo de punto validado (siempre 1-5)
- Tipo del valor devuelto:
- Muestra:
ValidationError – Si el tipo no está en el rango válido (1-5)
Ejemplo
Valores válidos:
# Frontera (por defecto) validate_point_type(None) # → 1 (frontera por defecto) validate_point_type(1) # → 1 (frontera explícito) # Consumo doméstico/comercial validate_point_type(2) # → 2 (consumo) # Generación eléctrica validate_point_type(3) # → 3 (generación) # Servicios del sistema validate_point_type(4) # → 4 (servicios auxiliares) validate_point_type(5) # → 5 (servicios auxiliares alt)
Casos que fallan (errores esperados):
# ❌ Tipo fuera de rango validate_point_type(6) # → ValidationError: "point_type debe ser 1 (frontera), 2 (consumo), 3 (generación) o 4 (servicios auxiliares)" # ❌ Tipo cero o negativo validate_point_type(0) # → ValidationError: "point_type debe ser 1 (frontera)..."
Uso típico por contexto:
# Consulta de consumo doméstico típico domestic_data = client.get_consumption( cups="ES001234567890123456AB", distributor_code="2", date_from="2024/01", date_to="2024/12", point_type=validate_point_type(2) # Consumo doméstico ) # Consulta de generación renovable generation_data = client.get_consumption( cups="ES001234567890123456AB", distributor_code="2", date_from="2024/01", date_to="2024/12", point_type=validate_point_type(3) # Generación )
Uso con constantes descriptivas:
from datadis_python.utils.constants import POINT_TYPES # Usar nombres descriptivos en lugar de números consumption_type = validate_point_type(POINT_TYPES["CONSUMPTION"]) # 2 generation_type = validate_point_type(POINT_TYPES["GENERATION"]) # 3 border_type = validate_point_type(POINT_TYPES["BORDER"]) # 1
Nota
Para la mayoría de usuarios domésticos y comerciales, el tipo 2 (CONSUMO) será el más relevante. Los tipos 3-5 son principalmente para instalaciones industriales y de servicios eléctricos.
- Technical Details:
Rango válido: 1-5 según normativa del sistema eléctrico español
Default conservador: None → 1 (frontera, valor neutro)
Validación estricta: Solo enteros en el rango específico
Mapeo normativo: Basado en clasificación oficial CNE/CNMC
Referencias
CNE: Comisión Nacional de Energía - Clasificación de puntos de medida
CNMC: Comisión Nacional de los Mercados y la Competencia
BOE: Normativa sobre tipos de punto en el sistema eléctrico
Ver también
datadis_python.utils.constants.POINT_TYPESpara constantes descriptivasDocumentación de Datadis sobre clasificación de puntos
Normativa española sobre medida y facturación eléctrica
Added in version 1.0: Validación de tipos de punto
Distinto en la versión 2.0: Añadido soporte para tipo 5 y documentación extendida
Utilidades HTTP
Cliente HTTP robusto y reutilizable para el SDK de Datadis.
Este módulo proporciona una clase HTTPClient especializada para realizar peticiones
HTTP a la API de Datadis con funcionalidades avanzadas como reintentos automáticos,
manejo de timeouts, gestión de headers y procesamiento de respuestas.
La clase está optimizada específicamente para las características de la API de Datadis: - Timeouts largos debido a la lentitud característica del servicio - Reintentos automáticos con backoff exponencial para manejar inestabilidad - Soporte para autenticación Bearer Token - Manejo especial de diferentes tipos de contenido (JSON, form-data, texto plano) - Procesamiento de respuestas con normalización de texto para caracteres especiales
- Características principales:
Gestión automática de reintentos: Backoff exponencial para timeouts y errores de red
Flexibilidad de contenido: Soporte para JSON y form-data según el endpoint
Manejo robusto de errores: Clasificación inteligente de errores HTTP
Integración con Pydantic: Preparado para validación de datos
Rate limiting integrado: Delays automáticos para evitar sobrecarga del servidor
Ejemplo
Uso básico del cliente HTTP:
from datadis_python.utils.http import HTTPClient
# Inicializar con configuración personalizada
client = HTTPClient(timeout=120, retries=5)
# Petición GET simple
response = client.make_request(
method="GET",
url="https://api.example.com/data",
params={"param1": "value1"}
)
# Petición POST con autenticación
client.set_auth_header("bearer_token_here")
response = client.make_request(
method="POST",
url="https://api.example.com/auth",
data={"username": "user", "password": "pass"},
use_form_data=True
)
# Cerrar recursos
client.close()
Uso como context manager (recomendado):
with HTTPClient(timeout=90, retries=3) as client:
client.set_auth_header("token")
response = client.make_request("GET", "https://api.example.com/data")
# El cliente se cierra automáticamente
Advertencia
Este cliente está específicamente optimizado para la API de Datadis. Para uso
general, considere usar directamente la biblioteca requests o un cliente
HTTP más genérico.
- author:
TacoronteRiveroCristian
- class datadis_python.utils.http.HTTPClient(timeout=60, retries=3)[fuente]
Bases:
objectCliente HTTP robusto especializado para la API de Datadis.
Esta clase proporciona una interfaz de alto nivel para realizar peticiones HTTP con características específicamente optimizadas para interactuar con la API de Datadis. Incluye manejo automático de reintentos, timeouts largos, gestión de autenticación y procesamiento especializado de respuestas.
- Optimizaciones para Datadis:
Timeouts largos: Por defecto 60s, recomendado 90-120s para Datadis
Reintentos robustos: Backoff exponencial con hasta 5 reintentos
Rate limiting: Delays automáticos para evitar sobrecarga del servidor
Manejo de encoding: Desactiva compresión gzip para evitar problemas
Procesamiento de texto: Normalización automática de caracteres especiales
- Estrategia de reintentos:
Errores de red y timeouts: Se reintentan automáticamente
Errores HTTP 4xx/5xx: Se propagan inmediatamente (no reintentos)
Backoff exponencial: 2s → 4s → 8s → 16s → 32s (máximo 10s para timeouts)
Error 401: Se propaga para permitir renovación de token
Ejemplo
Configuración típica para Datadis:
# Configuración robusta para API lenta de Datadis client = HTTPClient(timeout=120, retries=5) # Autenticación Bearer Token client.set_auth_header("jwt_token_from_datadis") # Petición con gestión automática de errores response = client.make_request( method="GET", url="https://datadis.es/api-private/api/get-supplies", params={"distributor_code": "2"} )
Diferentes tipos de contenido:
# Para autenticación (form-data) auth_response = client.make_request( method="POST", url="https://datadis.es/nikola-auth/tokens/login", data={"username": "12345678A", "password": "password"}, use_form_data=True ) # Para endpoints de datos (JSON, por defecto) data_response = client.make_request( method="GET", url="https://datadis.es/api-private/api/get-consumption-data", params={"cups": "ES001234567890123456AB"} )
- Parámetros:
Nota
La API de Datadis puede ser muy lenta (60-90 segundos) al procesar consultas complejas que requieren agregar datos de múltiples distribuidoras eléctricas.
Ver también
SimpleDatadisClientV1ySimpleDatadisClientV2para uso de alto nivelDocumentación oficial de Datadis para límites de rate limiting
- __init__(timeout=60, retries=3)[fuente]
Inicializa el cliente HTTP con configuración optimizada para Datadis.
Configura una sesión HTTP persistente con headers optimizados y timeouts apropiados para la API de Datadis. La configuración por defecto está pensada para un uso general, pero se recomienda ajustar los valores para Datadis.
- Configuración recomendada para Datadis:
timeout: 90-120 segundos (la API puede ser muy lenta)
retries: 3-5 reintentos (para manejar inestabilidad ocasional)
- Parámetros:
Ejemplo
Configuraciones típicas:
# Configuración conservadora (rápida, pocos reintentos) client = HTTPClient(timeout=60, retries=2) # Configuración robusta para Datadis (recomendada) client = HTTPClient(timeout=120, retries=5) # Configuración para desarrollo/testing (muy paciente) client = HTTPClient(timeout=180, retries=3)
- make_request(method, url, data=None, params=None, headers=None, use_form_data=False)[fuente]
Realiza una petición HTTP robusta con reintentos automáticos y manejo de errores.
Este método es el núcleo del cliente HTTP y maneja toda la lógica de peticiones incluyendo reintentos con backoff exponencial, manejo de diferentes tipos de contenido, rate limiting automático y procesamiento especializado de respuestas.
- Flujo de operación:
Rate limiting: Delay automático de 0.1s (excepto para autenticación)
Configuración de headers: Combina headers por defecto con personalizados
Selección de formato: JSON o form-data según
use_form_dataEjecución con reintentos: Hasta
self.retriesintentos con backoff exponencialProcesamiento de respuesta: Manejo especializado según tipo de contenido
- Estrategia de reintentos:
Errores de red/timeout: Reintentos con backoff: 2s → 4s → 8s → 16s…
Errores HTTP: Propagación inmediata sin reintentos
Máximo wait: 10 segundos entre reintentos para evitar timeouts excesivos
- Tipos de contenido soportados:
JSON (por defecto): Para la mayoría de endpoints de datos
Form-data: Para autenticación y algunos endpoints legacy
Detección automática: Basada en el parámetro
use_form_data
- Parámetros:
method (str) – Método HTTP a usar (GET, POST, PUT, DELETE, etc.)
url (str) – URL completa del endpoint a consultar
data (Optional[Dict[str, Any]]) – Datos a enviar en el cuerpo de la petición. Para GET se ignora, para POST se usa según
use_form_dataparams (Optional[Dict[str, Any]]) – Parámetros de query string a añadir a la URL
headers (Optional[Dict[str, str]]) – Headers HTTP adicionales a combinar con los por defecto. Los headers personalizados tienen prioridad sobre los por defecto
use_form_data (bool) – Si
True, envía datos como application/x-www-form-urlencoded. SiFalse(por defecto), envía como application/json
- Devuelve:
Respuesta procesada del servidor. El tipo depende del endpoint:
JWT tokens:
str(para endpoints de autenticación)Datos JSON:
Dict[str, Any]oList[Any](para endpoints de datos)Respuestas de texto:
str(para endpoints que no devuelven JSON)
- Tipo del valor devuelto:
- Muestra:
DatadisError – Si se agotan todos los reintentos por errores de red/timeouts
AuthenticationError – Si hay errores de autenticación (401)
APIError – Si la API devuelve errores HTTP (400, 403, 404, 500, etc.)
Ejemplo
Diferentes tipos de peticiones:
# GET con parámetros de query response = client.make_request( method="GET", url="https://datadis.es/api-private/api/get-supplies", params={"distributor_code": "2", "authorized_nif": "12345678A"} ) # POST con autenticación (form-data) token = client.make_request( method="POST", url="https://datadis.es/nikola-auth/tokens/login", data={"username": "12345678A", "password": "mi_password"}, use_form_data=True ) # GET con headers personalizados response = client.make_request( method="GET", url="https://datadis.es/api-private/api/get-consumption-data", params={"cups": "ES001234567890123456AB"}, headers={"Authorization": f"Bearer {token}"} )
Nota
El rate limiting automático (delay de 0.1s) se aplica a todas las peticiones excepto las de autenticación para evitar sobrecargar los servidores de Datadis que pueden tener límites de rate restrictivos.
Ver también
_handle_response()para detalles del procesamiento de respuestasLa normalización de texto se realiza automáticamente en respuestas JSON
- close()[fuente]
Cierra la sesión HTTP y libera recursos asociados.
Cierra explícitamente la sesión HTTP subyacente, liberando conexiones de red y otros recursos. Es una buena práctica llamar este método cuando se termina de usar el cliente, especialmente en aplicaciones de larga duración.
Este método es seguro de llamar múltiples veces y no genera errores si la sesión ya está cerrada.
Ejemplo
Uso manual de cierre:
client = HTTPClient(timeout=120, retries=5) try: # Usar cliente para peticiones... response = client.make_request("GET", "https://example.com") finally: # Asegurar limpieza de recursos client.close()
Uso como context manager (recomendado):
with HTTPClient(timeout=120, retries=5) as client: # Usar cliente... response = client.make_request("GET", "https://example.com") # client.close() se llama automáticamente
Nota
Cuando se usa el cliente como context manager (
withstatement), este método se llama automáticamente al salir del bloque, por lo que no es necesario llamarlo manualmente.- Tipo del valor devuelto:
None
- set_auth_header(token)[fuente]
Establece el header de autorización Bearer Token para todas las peticiones futuras.
Configura el token de autenticación que se añadirá automáticamente a todas las peticiones HTTP subsecuentes. Este método es típicamente llamado después de una autenticación exitosa para configurar el cliente para peticiones autenticadas.
El token se almacena en los headers de la sesión persistent, por lo que se aplicará automáticamente a todas las peticiones realizadas con este cliente hasta que se reemplace con un nuevo token o se elimine.
- Parámetros:
token (str) – Token JWT obtenido del endpoint de autenticación de Datadis. Debe ser un token válido sin el prefijo «Bearer « (se añade automáticamente)
- Tipo del valor devuelto:
None
Ejemplo
Configurar autenticación después del login:
client = HTTPClient(timeout=120, retries=5) # Autenticar y obtener token token = client.make_request( method="POST", url="https://datadis.es/nikola-auth/tokens/login", data={"username": "12345678A", "password": "mi_password"}, use_form_data=True ) # Configurar token para peticiones futuras client.set_auth_header(token) # Ahora todas las peticiones incluirán el header Authorization response = client.make_request( method="GET", url="https://datadis.es/api-private/api/get-supplies" )
Nota
El token se prefija automáticamente con «Bearer « según el estándar RFC 6750. No incluya este prefijo en el parámetro
token.Ver también
remove_auth_header()para eliminar la autenticaciónRFC 6750 para especificación completa de Bearer Token
- remove_auth_header()[fuente]
Elimina el header de autorización de todas las peticiones futuras.
Remueve el token de autenticación de los headers de la sesión, haciendo que las peticiones subsecuentes se realicen sin autenticación. Esto es útil para limpiar credenciales cuando se cambia de usuario o cuando se quiere realizar peticiones no autenticadas.
Esta operación es segura y no genera errores si no existe un header de autorización configurado.
Ejemplo
Limpiar autenticación para cambio de usuario:
# Usuario 1 autenticado client.set_auth_header(token_user1) data_user1 = client.make_request("GET", "/get-supplies") # Cambiar a usuario 2 client.remove_auth_header() # Limpiar credenciales anteriores token_user2 = client.make_request( "POST", "/nikola-auth/tokens/login", data={"username": "87654321B", "password": "other_password"}, use_form_data=True ) client.set_auth_header(token_user2) data_user2 = client.make_request("GET", "/get-supplies")
Peticiones sin autenticación:
client.remove_auth_header() # Ahora las peticiones no incluirán Authorization header public_data = client.make_request("GET", "/public-endpoint")
Nota
Después de llamar este método, las peticiones a endpoints que requieren autenticación fallarán con error 401 hasta que se establezca un nuevo token con
set_auth_header().Ver también
set_auth_header()para establecer nuevo token de autenticación
- Tipo del valor devuelto:
None
- __enter__()[fuente]
Método de entrada para context manager.
Permite usar el HTTPClient con la declaración
withde Python para gestión automática de recursos. Al entrar en el bloquewith, retorna la instancia del cliente lista para usar.- Devuelve:
La instancia del cliente HTTP configurada
- Tipo del valor devuelto:
Ejemplo
Uso como context manager:
with HTTPClient(timeout=120, retries=5) as client: # Configurar autenticación token = client.make_request( "POST", "/auth", data={"user": "12345678A", "pass": "password"}, use_form_data=True ) client.set_auth_header(token) # Realizar peticiones data = client.make_request("GET", "/api/data") # client.close() se llama automáticamente aquí
Nota
El uso como context manager es la forma recomendada de usar HTTPClient ya que garantiza la liberación adecuada de recursos de red.
- __exit__(exc_type, exc_val, exc_tb)[fuente]
Método de salida para context manager.
Se llama automáticamente al salir del bloque
with, garantizando que los recursos del cliente HTTP se liberen adecuadamente, independientemente de si el bloque se completó exitosamente o se produjo una excepción.- Parámetros:
exc_type – Tipo de excepción si ocurrió una excepción, None en caso contrario
exc_val – Valor de la excepción si ocurrió una excepción, None en caso contrario
exc_tb – Traceback de la excepción si ocurrió una excepción, None en caso contrario
Nota
Este método siempre retorna None, lo que significa que no suprime ninguna excepción que pueda haber ocurrido dentro del bloque
with. Las excepciones se propagan normalmente después de la limpieza de recursos.
Utilidades de Texto
Utilidades para normalización y procesamiento de texto de respuestas de Datadis.
Este módulo proporciona funciones especializadas para procesar y normalizar texto recibido de la API de Datadis, solucionando problemas comunes de encoding y caracteres especiales que pueden aparecer en las respuestas del servicio.
La API de Datadis frecuentemente devuelve texto con problemas de encoding, especialmente con caracteres españoles como tildes (á, é, í, ó, ú), eñes (ñ), cedillas (ç) y otros caracteres especiales. Este módulo proporciona herramientas robustas para detectar y corregir estos problemas automáticamente.
- Problemas comunes solucionados:
Doble codificación UTF-8: Secuencias como «Ãx93» en lugar de «Ó»
Caracteres especiales mal codificados: «EDISTRIBUCIÃx93N» → «EDISTRIBUCION»
Inconsistencias de encoding: Mezcla de encodings en una misma respuesta
Caracteres no ASCII: Normalización a ASCII para compatibilidad
- Funciones principales:
normalize_text(): Normaliza texto individual con corrección de encodingnormalize_dict_strings(): Normaliza recursivamente todas las cadenas en diccionariosnormalize_list_strings(): Normaliza recursivamente todas las cadenas en listasnormalize_api_response(): Función principal para normalizar respuestas completas
Ejemplo
Uso típico para procesar respuestas de Datadis:
from datadis_python.utils.text_utils import normalize_api_response
# Respuesta raw de Datadis con problemas de encoding
raw_response = {
"distributor": "EDISTRIBUCIÃ\x93N",
"address": "CALLE JOSÃπ MARTÃ\xadNEZ"
}
# Normalizar respuesta completa
clean_response = normalize_api_response(raw_response)
# Resultado: {"distributor": "EDISTRIBUCION", "address": "CALLE JOSE MARTINEZ"}
Normalización de texto individual:
from datadis_python.utils.text_utils import normalize_text
# Texto con problemas típicos de Datadis
problematic_text = "Málaga"
clean_text = normalize_text(problematic_text)
# Resultado: "Malaga"
Nota
Todas las funciones de este módulo son seguras de usar con datos None o de
tipos incorrectos - retornan el valor original sin modificar si no pueden procesarlo.
Advertencia
La normalización elimina permanentemente caracteres especiales y acentos. Si necesita preservar el texto original, haga una copia antes de aplicar estas funciones.
- author:
TacoronteRiveroCristian
- datadis_python.utils.text_utils.normalize_text(text)[fuente]
Normaliza texto removiendo tildes, caracteres especiales y corrigiendo problemas de encoding.
Esta función es el núcleo del sistema de normalización de texto del SDK. Aplica múltiples estrategias para corregir problemas comunes de encoding que aparecen frecuentemente en las respuestas de la API de Datadis, especialmente con caracteres españoles.
- Proceso de normalización:
Detección de doble codificación: Identifica secuencias como «Ãx93» (debe ser «Ó»)
Corrección de encoding: Recodifica usando latin-1 → UTF-8 cuando es necesario
Normalización Unicode: Aplica descomposición NFD para separar caracteres base de acentos
Eliminación de acentos: Convierte caracteres acentuados a su equivalente ASCII
Reemplazos específicos: Maneja caracteres especiales españoles (ñ, ç, etc.)
- Casos de uso típicos:
Nombres de distribuidores: «EDISTRIBUCIÃx93N» → «EDISTRIBUCION»
Direcciones: «CALLE JOSÃπ MARTÃxadNEZ» → «CALLE JOSE MARTINEZ»
Nombres de municipios: «Málaga», «Cádiz» → «Malaga», «Cadiz»
Códigos postales: Normalmente no necesitan normalización pero se procesan igual
- Parámetros:
text (str) – Texto a normalizar. Puede contener caracteres especiales, acentos, o problemas de doble codificación típicos de Datadis
- Devuelve:
Texto normalizado sin tildes, acentos ni caracteres especiales. Convertido completamente a caracteres ASCII seguros
- Tipo del valor devuelto:
Ejemplo
Problemas típicos de encoding de Datadis:
# Doble codificación UTF-8 normalize_text("EDISTRIBUCIÃ\x93N") # → "EDISTRIBUCION" normalize_text("Málaga") # → "Malaga" # Caracteres españoles normales normalize_text("Málaga") # → "Malaga" normalize_text("Coruña") # → "Coruna" normalize_text("Cáceres") # → "Caceres" # Caracteres especiales normalize_text("Peñíscola") # → "Peniscola" normalize_text("François") # → "Francois"
Casos extremos manejados:
# Texto ya normalizado - sin cambios normalize_text("MADRID") # → "MADRID" # Mezcla de problemas normalize_text("Cádiz - AndalucÃ\xada") # → "Cadiz - Andalucia"
Nota
La función es segura con datos de entrada inválidos - si recibe algo que no es string, retorna el valor original sin modificar. Esto evita errores en cadenas de procesamiento de datos.
Advertencia
La normalización es irreversible. Si necesita preservar el texto original con acentos, haga una copia antes de llamar esta función.
- Technical details:
Usa
unicodedata.normalize('NFD', text)para descomposición UnicodeAplica
encode('ascii', 'ignore')para eliminar caracteres no-ASCIIManeja específicamente problemas de doble codificación latin-1/UTF-8
Incluye tabla de reemplazos para caracteres que
unicodedatano maneja
Ver también
normalize_dict_strings()para normalizar diccionarios completosnormalize_api_response()para procesar respuestas completas de APIDocumentación de
unicodedatapara detalles sobre normalización Unicode
- datadis_python.utils.text_utils.normalize_dict_strings(data)[fuente]
Normaliza recursivamente todas las cadenas de texto en un diccionario.
Esta función recorre un diccionario de forma recursiva aplicando normalización de texto a todas las cadenas encontradas, manteniendo la estructura original del diccionario pero limpiando el contenido textual.
- Procesamiento recursivo:
Strings: Aplica
normalize_text()para limpiar caracteres especialesDiccionarios anidados: Procesa recursivamente con
normalize_dict_strings()Listas anidadas: Procesa recursivamente con
normalize_list_strings()Otros tipos: Se mantienen sin modificar (números, booleanos, None, etc.)
Esta función es especialmente útil para procesar respuestas JSON estructuradas de la API de Datadis donde los problemas de encoding pueden aparecer en cualquier campo de texto dentro de estructuras complejas.
- Parámetros:
data (Dict[str, Any]) – Diccionario con datos a normalizar. Puede contener estructuras anidadas complejas con strings que necesiten limpieza de caracteres especiales
- Devuelve:
Nuevo diccionario con la misma estructura pero con todos los strings normalizados. Los valores no-string se mantienen exactamente igual
- Tipo del valor devuelto:
Dict[str, Any]
Ejemplo
Normalizar respuesta estructurada de Datadis:
raw_supply_data = { "cups": "ES001234567890123456AB", "address": "CALLE JOSÃπ MARTÃ\xadNEZ", "distributor": "EDISTRIBUCIÃ\x93N", "postalCode": "28001", "province": "Mádrid", "details": { "city": "Málaga", "municipality": "AndalucÃ\xada" }, "contracts": [ {"startDate": "2024/01/01", "type": "Normal"} ], "active": True, "pointType": 2 } normalized_data = normalize_dict_strings(raw_supply_data) # Resultado: # { # "cups": "ES001234567890123456AB", # Sin cambio # "address": "CALLE JOSE MARTINEZ", # Normalizado # "distributor": "EDISTRIBUCION", # Normalizado # "postalCode": "28001", # Sin cambio # "province": "Madrid", # Normalizado # "details": { # "city": "Malaga", # Normalizado (recursivo) # "municipality": "Andalucia" # Normalizado (recursivo) # }, # "contracts": [ # {"startDate": "2024/01/01", "type": "Normal"} # Procesado recursivamente # ], # "active": True, # Sin cambio (boolean) # "pointType": 2 # Sin cambio (int) # }
Casos extremos manejados:
# Diccionario vacío normalize_dict_strings({}) # → {} # Diccionario con valores None normalize_dict_strings({"key": None}) # → {"key": None} # Anidación profunda deep_dict = {"level1": {"level2": {"text": "Málaga"}}} normalize_dict_strings(deep_dict) # → {"level1": {"level2": {"text": "Malaga"}}}
Nota
La función es segura con datos de entrada inválidos - si recibe algo que no es diccionario, retorna el valor original sin modificar. Esto permite su uso seguro en cadenas de procesamiento donde el tipo de dato puede ser incierto.
- Performance:
La función crea un nuevo diccionario en lugar de modificar el original, lo que la hace segura para uso concurrente pero puede consumir más memoria con estructuras muy grandes.
Ver también
normalize_text()para normalización de strings individualesnormalize_list_strings()para normalización de listasnormalize_api_response()para entrada principal de normalización
- datadis_python.utils.text_utils.normalize_list_strings(data)[fuente]
Normaliza recursivamente todas las cadenas de texto en una lista.
Esta función procesa listas de forma recursiva aplicando normalización de texto a todos los elementos string encontrados, mientras mantiene la estructura y orden original de la lista. Es especialmente útil para procesar arrays de datos de Datadis.
- Procesamiento recursivo por tipo:
Strings: Aplica
normalize_text()para limpiar caracteres especialesDiccionarios: Procesa recursivamente con
normalize_dict_strings()Listas anidadas: Procesa recursivamente con
normalize_list_strings()Otros tipos: Se mantienen sin modificar (números, booleanos, None, etc.)
- Casos de uso típicos en Datadis:
Arrays de suministros: Lista de objetos SupplyData con addresses problemáticas
Arrays de consumo: Lista de ConsumptionData con distributors mal codificados
Arrays mixtos: Listas que combinan strings, números y objetos
- Parámetros:
data (List[Any]) – Lista con datos a normalizar. Puede contener elementos de cualquier tipo, incluidas estructuras anidadas complejas
- Devuelve:
Nueva lista con la misma estructura y orden, pero con todos los strings normalizados. Los elementos no-string se mantienen exactamente igual
- Tipo del valor devuelto:
List[Any]
Ejemplo
Normalizar array de respuesta de Datadis:
raw_supplies_list = [ "EDISTRIBUCIÃ\x93N", # String simple problemático { "distributor": "Málaga Eléctrica", "address": "CALLE JOSÃπ MARTÃ\xadNEZ" }, ["Cádiz", "Sevillá"], # Lista anidada 42, # Número None, # Valor nulo True # Booleano ] normalized_list = normalize_list_strings(raw_supplies_list) # Resultado: # [ # "EDISTRIBUCION", # String normalizado # { # "distributor": "Malaga Electrica", # Dict normalizado recursivamente # "address": "CALLE JOSE MARTINEZ" # }, # ["Cadiz", "Sevilla"], # Lista normalizada recursivamente # 42, # Sin cambio (int) # None, # Sin cambio (None) # True # Sin cambio (bool) # ]
Array de objetos de consumo:
consumption_data = [ { "date": "2024/01/01", "consumptionKWh": 5.2, "distributor": "EDISTRIBUCIÃ\x93N" }, { "date": "2024/01/02", "consumptionKWh": 4.8, "distributor": "Málaga Eléctrica" } ] normalized_consumption = normalize_list_strings(consumption_data) # Solo los campos "distributor" se normalizan, fechas y números quedan igual
Casos extremos:
# Lista vacía normalize_list_strings([]) # → [] # Lista con solo valores no-string normalize_list_strings([1, 2, 3, True, None]) # → [1, 2, 3, True, None] # Anidación profunda deep_list = [[[["Málaga"]]]] normalize_list_strings(deep_list) # → [[[["Malaga"]]]]
Nota
La función es segura con datos de entrada inválidos - si recibe algo que no es lista, retorna el valor original sin modificar. Esto permite su uso seguro en cadenas de procesamiento de datos heterogéneos.
- Performance:
Crea una nueva lista en lugar de modificar la original, garantizando inmutabilidad pero puede consumir más memoria con listas muy grandes.
Ver también
normalize_text()para normalización de strings individualesnormalize_dict_strings()para normalización de diccionariosnormalize_api_response()para entrada principal de normalización
- datadis_python.utils.text_utils.normalize_api_response(response)[fuente]
Función principal para normalizar respuestas completas de la API de Datadis.
Esta es la función de entrada principal del sistema de normalización del SDK. Detecta automáticamente el tipo de respuesta (diccionario o lista) y aplica la estrategia de normalización correspondiente para limpiar todos los strings de problemas de encoding y caracteres especiales.
- Función de router inteligente:
Dict response: Usa
normalize_dict_strings()para procesamiento recursivoList response: Usa
normalize_list_strings()para procesamiento recursivoOtros tipos: Retorna sin modificar (para compatibilidad futura)
Esta función es llamada automáticamente por el
HTTPClientdel SDK, por lo que los usuarios normalmente no necesitan llamarla manualmente. Sin embargo, puede ser útil para procesar datos cacheados o respuestas guardadas localmente.- Casos de uso típicos:
Auto-procesamiento: Llamada automática por HTTPClient en todas las respuestas JSON
Procesamiento manual: Para limpiar datos guardados localmente o cacheados
Testing: Para normalizar respuestas mock en tests unitarios
Debugging: Para limpiar respuestas antes de análisis manual
- Parámetros:
response (Union[Dict[str, Any], List[Any]]) – Respuesta completa de la API de Datadis en formato JSON deserializado. Puede ser un diccionario (respuesta de objeto) o lista (respuesta de array)
- Devuelve:
Respuesta normalizada con la misma estructura pero con todos los strings limpios de problemas de encoding y caracteres especiales
- Tipo del valor devuelto:
Union[Dict[str, Any], List[Any]]
Ejemplo
Respuestas típicas de diferentes endpoints:
# Respuesta de get_supplies (lista de objetos) supplies_response = [ {"cups": "ES001...", "distributor": "EDISTRIBUCIÃ\x93N"}, {"cups": "ES002...", "distributor": "Málaga Eléctrica"} ] clean_supplies = normalize_api_response(supplies_response) # Resultado: [{"cups": "ES001...", "distributor": "EDISTRIBUCION"}, ...] # Respuesta de get_consumption (objeto con arrays) consumption_response = { "consumption": [ {"date": "2024/01/01", "distributor": "EDISTRIBUCIÃ\x93N"} ], "distributor_error": [] } clean_consumption = normalize_api_response(consumption_response) # Resultado: {"consumption": [{"date": "2024/01/01", "distributor": "EDISTRIBUCION"}], ...}
Procesamiento manual de datos guardados:
# Datos cacheados que pueden tener problemas de encoding cached_data = load_from_cache("datadis_supplies.json") clean_data = normalize_api_response(cached_data) # Ahora clean_data está libre de problemas de encoding
Uso en testing:
# Mock response con problemas simulados de Datadis mock_response = {"distributor": "EDISTRIBUCIÃ\x93N"} normalized_mock = normalize_api_response(mock_response) # Para tests con datos normalizados
Nota
Esta función es completamente segura con cualquier tipo de entrada. Si recibe datos que no puede procesar, los retorna sin modificar, garantizando que nunca cause errores en el pipeline de datos.
- Performance:
La función crea nuevas estructuras de datos en lugar de modificar las originales, lo que la hace segura para uso concurrente pero puede consumir más memoria con respuestas muy grandes (miles de registros).
- Integration:
Esta función está integrada automáticamente en la cadena de procesamiento de respuestas del
HTTPClient, por lo que todos los datos que llegan a los usuarios finales ya están normalizados.
Ver también
normalize_text()para normalización básica de stringsnormalize_dict_strings()para objetos JSONnormalize_list_strings()para arrays JSONdatadis_python.utils.http.HTTPClientdonde se integra automáticamente
Added in version 1.0: Normalización automática integrada en todo el SDK
Distinto en la versión 2.0: Mejorada detección de problemas de doble codificación UTF-8
Constantes
Constantes y configuraciones para el SDK de Datadis.
Este módulo centraliza todas las constantes utilizadas para interactuar con la API de Datadis, incluyendo URLs base, endpoints, códigos de distribuidores y configuraciones por defecto. Proporciona soporte para ambas versiones de la API (V1 y V2) manteniendo compatibilidad hacia atrás.
La API de Datadis es la plataforma oficial del gobierno español para acceder a datos de consumo eléctrico de las distribuidoras eléctricas. Este módulo abstrae toda la configuración necesaria para facilitar el desarrollo con el SDK.
- Organización de constantes:
URLs base: URLs principales de autenticación y API
Endpoints por versión: V1 (estables) y V2 (modernos con mejoras)
Códigos de distribuidores: Mapeo completo de todas las distribuidoras españolas
Configuraciones por defecto: Timeouts, reintentos y otros parámetros optimizados
Tipos de medida y punto: Constantes para clasificar datos eléctricos
Ejemplo
Uso básico de constantes:
from datadis_python.utils.constants import (
DATADIS_API_BASE,
API_V2_ENDPOINTS,
DISTRIBUTOR_CODES,
DEFAULT_TIMEOUT
)
# Construir URL de endpoint V2
supplies_url = f"{DATADIS_API_BASE}{API_V2_ENDPOINTS['supplies']}"
# Usar código de distribuidor por nombre
endesa_code = DISTRIBUTOR_CODES["E_DISTRIBUCION"] # "2"
# Configurar timeout por defecto
requests.get(url, timeout=DEFAULT_TIMEOUT)
Migración de V1 a V2:
# V1 (endpoints legacy)
v1_endpoint = API_V1_ENDPOINTS["consumption"] # "/get-consumption-data"
# V2 (endpoints modernos)
v2_endpoint = API_V2_ENDPOINTS["consumption"] # "/get-consumption-data-v2"
Nota
Los endpoints V2 incluyen funcionalidades adicionales como manejo de errores por distribuidor y estructuras de respuesta mejoradas. Se recomienda usar V2 para nuevas implementaciones.
Advertencia
La constante API_ENDPOINTS está marcada como DEPRECATED y se mantiene
solo para compatibilidad hacia atrás. Use API_V1_ENDPOINTS o API_V2_ENDPOINTS
según la versión de API que necesite.
- author:
TacoronteRiveroCristian
- datadis_python.utils.constants.DATADIS_BASE_URL = 'https://datadis.es'
URL principal del portal web de Datadis
- datadis_python.utils.constants.DATADIS_AUTH_URL = 'https://datadis.es/nikola-auth/tokens/login'
URL específica para autenticación de usuarios
- datadis_python.utils.constants.DATADIS_API_BASE = 'https://datadis.es/api-private/api'
URL base para todas las peticiones a la API privada de Datadis
- datadis_python.utils.constants.AUTH_ENDPOINTS = {'login': '/nikola-auth/tokens/login'}
Endpoints para operaciones de autenticación y gestión de tokens. Estos endpoints son compartidos por todas las versiones de la API.
- datadis_python.utils.constants.API_V1_ENDPOINTS = {'consumption': '/get-consumption-data', 'contracts': '/get-contract-detail', 'distributors': '/get-distributors-with-supplies', 'max_power': '/get-max-power', 'supplies': '/get-supplies'}
Endpoints para la API V1 de Datadis.
La versión 1 de la API proporciona respuestas directas (listas o diccionarios simples) sin estructuras de error por distribuidor. Es la versión original y más estable de la API, pero carece de algunas funcionalidades modernas.
- Características de V1:
Respuestas simples: List[SupplyData], List[ConsumptionData], etc.
Sin manejo de errores por distribuidor
Funcionalidad básica completa
Mayor compatibilidad con sistemas legacy
Ejemplo
Construir URL de endpoint V1:
from datadis_python.utils.constants import DATADIS_API_BASE, API_V1_ENDPOINTS supplies_url = f"{DATADIS_API_BASE}{API_V1_ENDPOINTS['supplies']}" # Resultado: "https://datadis.es/api-private/api/get-supplies"
- datadis_python.utils.constants.API_V2_ENDPOINTS = {'consumption': '/get-consumption-data-v2', 'contracts': '/get-contract-detail-v2', 'distributors': '/get-distributors-with-supplies-v2', 'max_power': '/get-max-power-v2', 'reactive_data': '/get-reactive-data-v2', 'supplies': '/get-supplies-v2'}
Endpoints para la API V2 de Datadis.
La versión 2 de la API introduce mejoras significativas incluyendo manejo robusto de errores por distribuidor, estructuras de respuesta tipadas y funcionalidades adicionales como datos de energía reactiva.
- Mejoras de V2 sobre V1:
Manejo de errores por distribuidor: Información detallada de fallos
Respuestas estructuradas: SuppliesResponse, ConsumptionResponse, etc.
Funcionalidad extendida: Acceso a datos de energía reactiva
Mejor compatibilidad: Preparada para futuras funcionalidades
Nota
Se recomienda usar la API V2 para nuevas implementaciones. Los endpoints V2 mantienen compatibilidad de interfaz con V1 pero devuelven estructuras de datos más robustas que incluyen información de errores detallada.
Ejemplo
Comparación de endpoints V1 vs V2:
# V1 - Respuesta simple v1_url = f"{DATADIS_API_BASE}{API_V1_ENDPOINTS['supplies']}" # GET /get-supplies → List[SupplyData] # V2 - Respuesta estructurada con manejo de errores v2_url = f"{DATADIS_API_BASE}{API_V2_ENDPOINTS['supplies']}" # GET /get-supplies-v2 → SuppliesResponse (includes distributor_error)
- datadis_python.utils.constants.AUTHORIZATION_ENDPOINTS = {'cancel_authorization': '/cancel-authorization', 'list_authorization': '/list-authorization', 'new_authorization': '/new-authorization'}
Endpoints para gestión de autorizaciones entre usuarios.
Estos endpoints permiten gestionar autorizaciones para que un usuario pueda consultar los datos de consumo de otro usuario (por ejemplo, un gestor energético consultando datos de sus clientes con autorización previa).
- Flujo de autorización:
new_authorization: Crear nueva autorización
list_authorization: Listar autorizaciones activas
cancel_authorization: Cancelar autorización existente
Nota
Esta funcionalidad requiere configuración especial en la cuenta de Datadis y está orientada a usuarios avanzados como gestores energéticos o empresas de servicios energéticos.
- datadis_python.utils.constants.DEFAULT_TIMEOUT = 90
Timeout por defecto para peticiones HTTP en segundos.
El valor de 90 segundos está optimizado para la API de Datadis, que puede ser notablemente lenta debido a la necesidad de consultar múltiples sistemas de distribuidoras eléctricas en tiempo real.
Nota
La API de Datadis puede tardar hasta 60-80 segundos en responder consultas complejas, especialmente cuando debe agregar datos de múltiples distribuidores o procesar rangos de fechas extensos.
- datadis_python.utils.constants.MAX_RETRIES = 5
Número máximo de reintentos automáticos para peticiones fallidas.
Se incrementa a 5 reintentos debido a la naturaleza inestable de algunos endpoints de Datadis y la dependencia de sistemas externos de las distribuidoras eléctricas. Los reintentos usan backoff exponencial para evitar sobrecargar el servidor.
- datadis_python.utils.constants.TOKEN_EXPIRY_HOURS = 24
Duración estimada de validez de los tokens de autenticación en horas.
Los tokens de Datadis tienen una validez limitada. Aunque la duración exacta no está documentada oficialmente, la experiencia práctica sugiere que expiran en aproximadamente 24 horas.
Nota
El SDK maneja automáticamente la renovación de tokens cuando detecta errores 401 (Unauthorized), por lo que este valor es principalmente informativo.
- datadis_python.utils.constants.MEASUREMENT_TYPES = {'CONSUMPTION': 0, 'GENERATION': 1}
Constantes para los tipos de medida eléctrica soportados por Datadis.
La API de Datadis distingue entre consumo (energía tomada de la red) y generación (energía inyectada a la red, típicamente de instalaciones fotovoltaicas u otras fuentes renovables).
- Valores válidos:
CONSUMPTION (0): Energía consumida desde la red eléctrica
GENERATION (1): Energía generada e inyectada a la red
Ejemplo
Usar constantes de tipo de medida:
from datadis_python.utils.constants import MEASUREMENT_TYPES # Obtener datos de consumo consumption_data = client.get_consumption( cups="ES001234567890123456AB", distributor_code="2", date_from="2024/01", date_to="2024/12", measurement_type=MEASUREMENT_TYPES["CONSUMPTION"] # 0 ) # Obtener datos de generación (ej: paneles solares) generation_data = client.get_consumption( cups="ES001234567890123456AB", distributor_code="2", date_from="2024/01", date_to="2024/12", measurement_type=MEASUREMENT_TYPES["GENERATION"] # 1 )
- datadis_python.utils.constants.POINT_TYPES = {'AUXILIARY_SERVICES': 4, 'AUXILIARY_SERVICES_ALT': 5, 'BORDER': 1, 'CONSUMPTION': 2, 'GENERATION': 3}
Constantes para los tipos de punto de medida eléctrica según normativa española.
Los tipos de punto definen la naturaleza y propósito del punto de medida eléctrica según la normativa del sistema eléctrico español. Cada tipo tiene características específicas y se usa en diferentes contextos de la red eléctrica.
- Tipos de punto válidos:
BORDER (1): Punto de frontera - Intercambio entre sistemas
CONSUMPTION (2): Punto de consumo - Usuarios finales domésticos/comerciales
GENERATION (3): Punto de generación - Plantas de generación eléctrica
AUXILIARY_SERVICES (4): Servicios auxiliares - Equipos de soporte al sistema
AUXILIARY_SERVICES_ALT (5): Servicios auxiliares alternativos
Nota
El tipo de punto más común para usuarios domésticos y comerciales es CONSUMPTION (2). Los tipos de generación se usan principalmente para instalaciones con autoconsumo que inyectan energía a la red.
Ejemplo
Filtrar por tipo de punto específico:
from datadis_python.utils.constants import POINT_TYPES # Datos de consumo doméstico típico consumption_data = client.get_consumption( cups="ES001234567890123456AB", distributor_code="2", date_from="2024/01", date_to="2024/12", point_type=POINT_TYPES["CONSUMPTION"] # 2 ) # Datos de instalación de generación generation_data = client.get_consumption( cups="ES001234567890123456AB", distributor_code="2", date_from="2024/01", date_to="2024/12", point_type=POINT_TYPES["GENERATION"] # 3 )
- datadis_python.utils.constants.DISTRIBUTOR_CODES = {'ASEME': '4', 'CIDE': '7', 'EOSA': '6', 'E_DISTRIBUCION': '2', 'E_REDES': '3', 'IDE': '8', 'UFD': '5', 'VIESGO': '1'}
Mapeo completo de todas las distribuidoras eléctricas españolas con sus códigos oficiales.
Este diccionario proporciona el mapeo entre nombres de distribuidoras y sus códigos numéricos oficiales utilizados por la API de Datadis. Los códigos son asignados por el sistema eléctrico español y son únicos para cada distribuidora.
- Distribuidoras por código:
«1» - VIESGO: Cantabria, Asturias (norte de España)
«2» - E-DISTRIBUCIÓN: Filial de Endesa, cobertura nacional amplia
«3» - E-REDES: Galicia y zonas de Castilla y León
«4» - ASEME: Ciudad autónoma de Melilla
«5» - UFD (Naturgy): Cobertura nacional, especialmente este y sur
«6» - EOSA: Aragón y zonas del noreste
«7» - CIDE: Ciudad autónoma de Ceuta
«8» - IDE: Islas Baleares
- Coverage geográfico:
Nacional: E-distribución (Endesa), UFD (Naturgy)
Regional: Viesgo (norte), E-redes (noroeste), EOSA (noreste)
Insular/Local: IDE (Baleares), ASEME (Melilla), CIDE (Ceuta)
Ejemplo
Usar nombres de distribuidoras en lugar de códigos:
from datadis_python.utils.constants import DISTRIBUTOR_CODES # Buscar suministros de Endesa por nombre endesa_code = DISTRIBUTOR_CODES["E_DISTRIBUCION"] # "2" supplies = client.get_supplies(distributor_code=endesa_code) # Buscar suministros de Naturgy por nombre naturgy_code = DISTRIBUTOR_CODES["UFD"] # "5" supplies = client.get_supplies(distributor_code=naturgy_code) # Iterar sobre todas las distribuidoras for name, code in DISTRIBUTOR_CODES.items(): print(f"Distribuidor {name}: código {code}")
Nota
Los códigos de distribuidor son strings (no enteros) para mantener consistencia con la API de Datadis. El SDK acepta tanto strings como enteros y convierte automáticamente al tipo correcto.
- datadis_python.utils.constants.API_ENDPOINTS = {'cancel_authorization': '/cancel-authorization', 'consumption': '/get-consumption-data', 'consumption_v2': '/get-consumption-data-v2', 'contracts': '/get-contract-detail', 'contracts_v2': '/get-contract-detail-v2', 'distributors': '/get-distributors-with-supplies', 'distributors_v2': '/get-distributors-with-supplies-v2', 'list_authorization': '/list-authorization', 'login': '/nikola-auth/tokens/login', 'max_power': '/get-max-power', 'max_power_v2': '/get-max-power-v2', 'new_authorization': '/new-authorization', 'reactive_data_v2': '/get-reactive-data-v2', 'supplies': '/get-supplies', 'supplies_v2': '/get-supplies-v2'}
Diccionario unificado de endpoints para compatibilidad hacia atrás.
Obsoleto desde la versión 2.0: Use
API_V1_ENDPOINTSoAPI_V2_ENDPOINTSsegún la versión de API que necesite. Este diccionario se mantiene solo para compatibilidad con código legacy y será eliminado en futuras versiones.Este diccionario combina todos los endpoints disponibles en un solo lugar, pero no proporciona la claridad de separación entre versiones que ofrecen las constantes específicas por versión.
- Problemas con este enfoque:
Ambigüedad de versión: No queda claro qué versión de API se está usando
Mantenimiento complejo: Requiere sincronización manual entre versiones
Falta de tipado: No aprovecha las mejoras de tipos de V2
- Migration path:
Reemplazar uso de API_ENDPOINTS:
# ❌ Deprecated - No usar old_endpoint = API_ENDPOINTS["supplies"] # ✅ Recomendado - Usar versión específica v1_endpoint = API_V1_ENDPOINTS["supplies"] # Para compatibilidad v2_endpoint = API_V2_ENDPOINTS["supplies"] # Para nuevas funcionalidades
Advertencia
Esta constante será eliminada en la versión 3.0 del SDK. Migre su código para usar las constantes específicas por versión.
Cliente Base (Avanzado)
- class datadis_python.client.base.BaseDatadisClient(username, password, timeout=90, retries=5)[fuente]
Bases:
ABCCliente base abstracto con funcionalidad común para todas las versiones.
- Parámetros:
- authenticate()[fuente]
Autentica con la API y obtiene token de acceso.
- Muestra:
AuthenticationError – Si las credenciales son inválidas
APIError – Si ocurre un error en la comunicación con la API
- Tipo del valor devuelto:
None
- ensure_authenticated()[fuente]
Asegura que el cliente está autenticado con un token válido.
Renueva automáticamente el token si ha expirado o está próximo a expirar.
- Tipo del valor devuelto:
None
- make_authenticated_request(method, endpoint, data=None, params=None)[fuente]
Realiza una petición autenticada a la API.
- Parámetros:
- Devuelve:
Respuesta de la API
- Tipo del valor devuelto:
- Muestra:
AuthenticationError – Si fallan las credenciales
APIError – Si ocurre un error en la API
- close()[fuente]
Cierra la sesión y libera recursos.
Limpia el token de autenticación y cierra las conexiones HTTP.
- Tipo del valor devuelto:
None
- __enter__()[fuente]
Entrada del context manager.
- Devuelve:
Instancia del cliente
- Tipo del valor devuelto:
- __exit__(exc_type, exc_val, exc_tb)[fuente]
Salida del context manager.
- Parámetros:
exc_type (Optional[type]) – Tipo de excepción
exc_val (Optional[BaseException]) – Valor de la excepción
exc_tb (Optional[TracebackType]) – Traceback de la excepción
- abstractmethod get_supplies(authorized_nif=None, distributor_code=None)[fuente]
Obtiene puntos de suministro.
- abstractmethod get_distributors(authorized_nif=None)[fuente]
Obtiene distribuidores.
- Parámetros:
authorized_nif (Optional[str]) – NIF autorizado para la consulta
- Devuelve:
Lista de distribuidores
- Tipo del valor devuelto:
Any
- abstractmethod get_contract_detail(cups, distributor_code, authorized_nif=None)[fuente]
Obtiene detalle del contrato.
- abstractmethod get_consumption(cups, distributor_code, date_from, date_to, measurement_type=0, point_type=None, authorized_nif=None)[fuente]
Obtiene datos de consumo.
- Parámetros:
cups (str) – Código CUPS del punto de suministro
distributor_code (str) – Código de la distribuidora
date_from (str) – Fecha de inicio (YYYY-MM-DD)
date_to (str) – Fecha de fin (YYYY-MM-DD)
measurement_type (int) – Tipo de medida
point_type (Optional[int]) – Tipo de punto
authorized_nif (Optional[str]) – NIF autorizado para la consulta
- Devuelve:
Datos de consumo
- Tipo del valor devuelto:
Any