datadis_python.models package

Submodules

Module contents

Modelos de datos utilizados en el SDK de Datadis.

author:

TacoronteRiveroCristian

class datadis_python.models.ConsumptionData(*, cups, date, time, consumptionKWh, obtainMethod, surplusEnergyKWh=None, generationEnergyKWh=None, selfConsumptionEnergyKWh=None)[fuente]

Bases: BaseModel

Modelo 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_kwh

Ver también

  • ConsumptionResponse - Respuesta estructurada de la API V2

  • SimpleDatadisClientV1.get_consumption() - Obtener datos V1

  • SimpleDatadisClientV2.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].

cups: str
date: str
time: str
consumption_kwh: float
obtain_method: str
surplus_energy_kwh: float | None
generation_energy_kwh: float | None
self_consumption_energy_kwh: float | None
class datadis_python.models.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: BaseModel

Modelo 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)

  • contractedPowerkW (List[float])

  • timeDiscrimination (str | None)

  • modePowerControl (str)

  • startDate (str)

  • endDate (str | None)

  • codeFare (str)

  • selfConsumptionTypeCode (str | None)

  • selfConsumptionTypeDesc (str | None)

  • partitionCoefficient (float | None)

  • installedCapacityKW (float | None)

  • dateOwner (List[DateOwner] | 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_coefficient debe sumar 1.0 entre todos los participantes de la instalación compartida.

Ver también

  • DateOwner - Modelo para períodos de propiedad

  • ContractResponse - Respuesta estructurada de la API V2

  • SimpleDatadisClientV2.get_contract_detail() - Obtener datos contractuales

  • RD 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].

cups: str
distributor: str
marketer: str | None
tension: str
access_fare: str
province: str
municipality: str
postal_code: str
contracted_power_kw: List[float]
time_discrimination: str | None
mode_power_control: str
start_date: str
end_date: str | None
code_fare: str
self_consumption_type_code: str | None
self_consumption_type_desc: str | None
section: str | None
subsection: str | None
partition_coefficient: float | None
cau: str | None
installed_capacity_kw: float | None
date_owner: List[DateOwner] | None
last_marketer_date: str | None
max_power_install: str | None
class datadis_python.models.DateOwner(*, startDate, endDate)[fuente]

Bases: BaseModel

Modelo Pydantic para períodos de propiedad de un punto de suministro.

Representa un período temporal durante el cual el usuario autenticado ha sido propietario o titular de un punto de suministro (CUPS). Esta información es especialmente relevante para consultas históricas y para entender los períodos de responsabilidad sobre un contrato eléctrico.

Casos de uso comunes:
  • Cambios de propiedad: Transferencias de titularidad entre personas

  • Alquileres: Períodos como inquilino vs. propietario

  • Herencias: Cambios de titularidad por herencia

  • Compraventa: Traspaso en ventas de inmuebles

Ejemplo

Período de propiedad típico:

from datadis_python.models.contract import DateOwner

# Período como propietario
ownership = DateOwner(
    startDate="2020/01/15",
    endDate="2024/06/30"
)

print(f"Propietario desde: {ownership.start_date}")
print(f"Hasta: {ownership.end_date}")

Múltiples períodos (en contexto de ContractData):

# Usuario fue propietario en dos períodos diferentes
periods = [
    DateOwner(startDate="2018/03/01", endDate="2019/12/31"),
    DateOwner(startDate="2022/06/01", endDate="2024/08/15")
]

for i, period in enumerate(periods, 1):
    print(f"Período {i}: {period.start_date} - {period.end_date}")
Parámetros:
  • start_date (str) – Fecha de inicio del período de propiedad en formato YYYY/MM/DD. Fecha desde la cual el usuario es considerado propietario o titular del punto de suministro eléctrico

  • end_date (str) – Fecha de finalización del período de propiedad en formato YYYY/MM/DD. Fecha hasta la cual el usuario mantuvo la titularidad del suministro

  • startDate (str)

  • endDate (str)

Muestra:

ValidationError – Si las fechas no tienen formato válido o faltan campos obligatorios

Nota

Las fechas deben seguir el formato estándar de Datadis: YYYY/MM/DD (año/mes/día)

Ver también

  • ContractData - Modelo que incluye lista de períodos de propiedad

  • SimpleDatadisClientV2.get_contract_detail() - Obtener información contractual completa

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].

start_date: str
end_date: str
class datadis_python.models.DistributorData(*, distributorCodes)[fuente]

Bases: BaseModel

Modelo 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:
  • distributor_codes (List[str]) – Lista de códigos de distribuidoras donde el usuario tiene puntos de suministro activos. Cada código identifica una empresa distribuidora específica del sistema eléctrico español

  • distributorCodes (List[str])

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 V2

  • SimpleDatadisClientV1.get_distributors() - Obtener distribuidoras V1

  • SimpleDatadisClientV2.get_distributors() - Obtener distribuidoras V2

  • Los 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].

distributor_codes: List[str]
class datadis_python.models.MaxPowerData(*, cups, date, time, maxPower, period)[fuente]

Bases: BaseModel

Modelo 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 / 1000

Truco

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 V2

  • SimpleDatadisClientV1.get_max_power() - Obtener datos V1

  • SimpleDatadisClientV2.get_max_power() - Obtener datos V2

  • ContractData - 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].

cups: str
date: str
time: str
max_power: float
period: str
class datadis_python.models.ReactiveData(*, reactiveEnergy)[fuente]

Bases: BaseModel

Modelo 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

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].

reactive_energy: ReactiveEnergyData
class datadis_python.models.ReactiveEnergyData(*, cups, energy, code=None, code_desc=None)[fuente]

Bases: BaseModel

Modelo Pydantic completo para datos de energía reactiva de un punto de suministro.

Contenedor principal que agrupa toda la información de energía reactiva de un CUPS específico, incluyendo las mediciones por períodos tarifarios y información de estado o errores. Este modelo representa la respuesta detallada de la API para consultas de energía reactiva, exclusiva de la versión V2.

Información incluida:
  • Identificación: Código CUPS del punto de suministro

  • Datos temporales: Lista de períodos con mediciones mensuales

  • Control de errores: Códigos y descripciones de posibles incidencias

  • Validación: Estructura validada con Pydantic para garantizar integridad

Estados posibles de la respuesta:
  • Datos completos: code=None, mediciones disponibles en energy

  • Sin datos: Períodos sin mediciones (energy vacío)

  • Error parcial: Algunos períodos con datos, otros con errores

  • Error total: code!=None con descripción del problema

Ejemplo

Uso básico con datos completos:

from datadis_python.models.reactive import ReactiveEnergyData, ReactiveEnergyPeriod

# Datos de energía reactiva de una instalación industrial
reactive_data = ReactiveEnergyData(
    cups="ES001234567890123456AB",
    energy=[
        ReactiveEnergyPeriod(
            date="2024/01",
            energy_p1=156.8, energy_p2=98.5,
            energy_p3=203.2, energy_p5=89.1
        ),
        ReactiveEnergyPeriod(
            date="2024/02",
            energy_p1=142.3, energy_p2=87.9,
            energy_p3=189.5, energy_p5=76.8
        )
    ],
    code=None,  # Sin errores
    code_desc=None
)

print(f"CUPS: {reactive_data.cups}")
print(f"Períodos disponibles: {len(reactive_data.energy)}")

# Análisis mensual
for period in reactive_data.energy:
    total_month = sum(filter(None, [
        period.energy_p1, period.energy_p2,
        period.energy_p3, period.energy_p5
    ]))
    print(f"Mes {period.date}: {total_month:.1f} kVArh")

Manejo de errores en la respuesta:

# Caso con error en los datos
reactive_error = ReactiveEnergyData(
    cups="ES009876543210987654AB",
    energy=[],  # Sin datos
    code="ERR_404",
    code_desc="No hay datos de energía reactiva para el período solicitado"
)

if reactive_error.code:
    print(f"Error {reactive_error.code}: {reactive_error.code_desc}")
    print("No se pueden analizar datos de energía reactiva")
else:
    # Procesar datos normalmente
    pass

Análisis de eficiencia energética:

# Calcular tendencias de energía reactiva
monthly_totals = []
for period in reactive_data.energy:
    monthly_reactive = sum(filter(None, [
        period.energy_p1, period.energy_p2,
        period.energy_p3, period.energy_p4,
        period.energy_p5, period.energy_p6
    ]))
    monthly_totals.append(monthly_reactive)

if monthly_totals:
    avg_reactive = sum(monthly_totals) / len(monthly_totals)
    print(f"Promedio mensual energía reactiva: {avg_reactive:.1f} kVArh")

    # Detectar tendencias
    if len(monthly_totals) >= 2:
        trend = monthly_totals[-1] - monthly_totals[0]
        if trend > 0:
            print("📈 Tendencia creciente - revisar factor de potencia")
        else:
            print("📉 Tendencia decreciente - mejorando eficiencia")

Comparación entre períodos tarifarios:

# Identificar el período con mayor consumo reactivo
period_totals = {}
for monthly_data in reactive_data.energy:
    for p_num, energy in [
        ("P1", monthly_data.energy_p1), ("P2", monthly_data.energy_p2),
        ("P3", monthly_data.energy_p3), ("P4", monthly_data.energy_p4),
        ("P5", monthly_data.energy_p5), ("P6", monthly_data.energy_p6)
    ]:
        if energy is not None:
            period_totals[p_num] = period_totals.get(p_num, 0) + energy

if period_totals:
    max_period = max(period_totals, key=period_totals.get)
    print(f"Período con mayor consumo reactivo: {max_period}")
    print(f"Total acumulado: {period_totals[max_period]:.1f} kVArh")
Parámetros:
  • cups (str) – Código CUPS del punto de suministro para el cual se consultaron los datos de energía reactiva. Identificador único del punto de conexión

  • energy (List[ReactiveEnergyPeriod]) – Lista de objetos ReactiveEnergyPeriod con las mediciones mensuales de energía reactiva desglosadas por períodos tarifarios

  • code (Optional[str]) – Código de error o estado. None si la consulta fue exitosa, string con código específico si hubo problemas en la obtención de datos

  • code_desc (Optional[str]) – Descripción detallada del error o estado. Proporciona información legible sobre el problema específico encontrado durante la consulta

Muestra:

ValidationError – Si el CUPS tiene formato inválido o faltan campos obligatorios

Nota

La energía reactiva solo está disponible en la API V2 y requiere que el contador inteligente del suministro soporte medición de energía reactiva.

Advertencia

Un code diferente de None indica problemas en la obtención de datos. Siempre verificar este campo antes de procesar las mediciones.

Ver también

  • ReactiveEnergyPeriod - Modelo de mediciones por período

  • ReactiveResponse - Respuesta completa con errores de distribuidor

  • SimpleDatadisClientV2.get_reactive_data() - Método para obtener estos datos

Added in version 2.0: Funcionalidad exclusiva de la API V2 para análisis avanzado de eficiencia energética

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].

cups: str
energy: List[ReactiveEnergyPeriod]
code: str | None
code_desc: str | None
class datadis_python.models.ReactiveEnergyPeriod(*, date, energy_p1=None, energy_p2=None, energy_p3=None, energy_p4=None, energy_p5=None, energy_p6=None)[fuente]

Bases: BaseModel

Modelo Pydantic para datos de energía reactiva por período tarifario.

Representa las mediciones de energía reactiva registradas en un mes específico, desglosadas por los diferentes períodos tarifarios establecidos en el sistema eléctrico español. La energía reactiva es necesaria para el funcionamiento de equipos inductivos pero no realiza trabajo útil, y su exceso puede penalizarse.

Conceptos fundamentales de energía reactiva:
  • Energía activa: Realiza trabajo útil (kWh) - la que se factura normalmente

  • Energía reactiva: Necesaria para equipos inductivos (kVArh) - puede penalizarse

  • Factor de potencia: Relación entre energía activa y aparente (cos φ)

  • Penalizaciones: Se aplican si el factor de potencia es inferior a 0.95

Equipos que consumen energía reactiva:
  • Motores eléctricos: Especialmente en arranque y con poca carga

  • Transformadores: En vacío o con poca carga

  • Lámparas fluorescentes: Sin condensador de corrección

  • Soldadores de arco: Durante el funcionamiento

  • Hornos de inducción: Para calentamiento industrial

Períodos tarifarios en discriminación horaria:
  • P1: Punta (horas de mayor demanda nacional)

  • P2: Llano alto (horas intermedias altas)

  • P3: Llano (horas intermedias)

  • P4: Valle alto (horas intermedias bajas)

  • P5: Valle (horas de menor demanda)

  • P6: Supervalle (madrugada, solo algunas tarifas)

Ejemplo

Análisis de consumo reactivo mensual:

from datadis_python.models.reactive import ReactiveEnergyPeriod

# Datos de energía reactiva de una industria en enero
reactive_period = ReactiveEnergyPeriod(
    date="2024/01",
    energy_p1=125.5,  # kVArh en horas punta
    energy_p2=89.2,   # kVArh en llano alto
    energy_p3=156.8,  # kVArh en llano
    energy_p4=None,   # No aplica para esta tarifa
    energy_p5=78.3,   # kVArh en valle
    energy_p6=None    # No aplica
)

# Calcular total reactivo mensual
total_reactive = sum(filter(None, [
    reactive_period.energy_p1, reactive_period.energy_p2,
    reactive_period.energy_p3, reactive_period.energy_p5
]))

print(f"Mes: {reactive_period.date}")
print(f"Total energía reactiva: {total_reactive:.1f} kVArh")

Detección de penalizaciones potenciales:

# Supongamos 1000 kWh de energía activa en el mismo período
energia_activa = 1000.0  # kWh del mes
energia_reactiva = total_reactive  # del ejemplo anterior

# Calcular factor de potencia aproximado
import math
factor_potencia = energia_activa / math.sqrt(energia_activa**2 + energia_reactiva**2)

print(f"Factor de potencia estimado: {factor_potencia:.3f}")

if factor_potencia < 0.95:
    print("⚠️ Riesgo de penalización por energía reactiva")
    print("Considere instalar condensadores de corrección")
else:
    print("✅ Factor de potencia dentro de límites normales")

Análisis por períodos tarifarios:

periods_data = [
    ("P1 (Punta)", reactive_period.energy_p1),
    ("P2 (Llano alto)", reactive_period.energy_p2),
    ("P3 (Llano)", reactive_period.energy_p3),
    ("P5 (Valle)", reactive_period.energy_p5)
]

print("Consumo reactivo por período:")
for period_name, energy in periods_data:
    if energy is not None:
        print(f"- {period_name}: {energy:.1f} kVArh")
Parámetros:
  • date (str) – Fecha del período en formato YYYY/MM. Corresponde al mes para el cual se registraron las mediciones de energía reactiva

  • energy_p1 (Optional[float]) – Energía reactiva consumida durante el período P1 (Punta) expresada en kilovatios-amperio reactivo hora (kVArh)

  • energy_p2 (Optional[float]) – Energía reactiva consumida durante el período P2 (Llano alto) en kVArh

  • energy_p3 (Optional[float]) – Energía reactiva consumida durante el período P3 (Llano) en kVArh

  • energy_p4 (Optional[float]) – Energía reactiva consumida durante el período P4 (Valle alto) en kVArh. Puede ser None si la tarifa no incluye este período

  • energy_p5 (Optional[float]) – Energía reactiva consumida durante el período P5 (Valle) en kVArh

  • energy_p6 (Optional[float]) – Energía reactiva consumida durante el período P6 (Supervalle) en kVArh. Solo aplicable en tarifas con discriminación horaria extendida

Muestra:

ValidationError – Si la fecha no tiene formato válido YYYY/MM

Nota

Los valores None indican que el período tarifario no aplica para la tarifa específica del suministro o que no hay datos disponibles para ese período.

Truco

Para reducir la energía reactiva: instale condensadores de corrección, optimice el funcionamiento de motores y reemplace equipos ineficientes.

Ver también

  • ReactiveEnergyData - Contenedor completo de datos reactivos

  • ReactiveResponse - Respuesta de la API V2 con manejo de errores

  • Factor de potencia objetivo: cos φ ≥ 0.95 para evitar penalizaciones

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].

date: str
energy_p1: float | None
energy_p2: float | None
energy_p3: float | None
energy_p4: float | None
energy_p5: float | None
energy_p6: float | None
class datadis_python.models.ReactiveResponse(*, reactiveEnergy, distributorError=<factory>)[fuente]

Bases: BaseModel

Modelo Pydantic completo para respuesta estructurada de energía reactiva V2.

Respuesta completa y robusta del endpoint get_reactive_data de la API V2, que incluye tanto los datos de energía reactiva como información detallada sobre errores específicos por distribuidor. Esta estructura permite manejar casos donde algunos distribuidores proporcionan datos correctamente mientras otros experimentan problemas técnicos.

Características de la respuesta V2:
  • Datos principales: Información completa de energía reactiva

  • Manejo granular de errores: Errores específicos por distribuidor

  • Robustez: Respuesta parcial en caso de fallos de algunos distribuidores

  • Información detallada: Códigos y descripciones específicas de errores

Ventajas sobre respuestas simples:
  • Transparencia: Visibilidad completa de problemas por distribuidor

  • Resiliencia: Datos útiles incluso con errores parciales

  • Diagnóstico: Información detallada para resolución de problemas

  • Consistencia: Estructura uniforme independientemente del estado

Ejemplo

Respuesta exitosa completa:

from datadis_python.models.reactive import ReactiveResponse
from datadis_python.client.v2 import SimpleDatadisClientV2

with SimpleDatadisClientV2("12345678A", "password") as client:
    response = client.get_reactive_data(
        cups="ES001234567890123456AB",
        distributor_code="2",
        date_from="2024/01",
        date_to="2024/06"
    )

# Verificar respuesta completa
if not response.distributor_error:
    print("✅ Datos obtenidos sin errores")
    reactive_data = response.reactive_energy

    print(f"CUPS: {reactive_data.cups}")
    print(f"Períodos con datos: {len(reactive_data.energy)}")

    # Procesar datos de energía reactiva
    for period in reactive_data.energy:
        total_reactive = sum(filter(None, [
            period.energy_p1, period.energy_p2, period.energy_p3,
            period.energy_p4, period.energy_p5, period.energy_p6
        ]))
        print(f"Mes {period.date}: {total_reactive:.1f} kVArh")
else:
    print("❌ Errores detectados por distribuidor")

Manejo de errores por distribuidor:

# Respuesta con errores específicos
if response.distributor_error:
    print("Errores por distribuidor:")
    for error in response.distributor_error:
        print(f"- {error.distributor_name} (código {error.distributor_code}):")
        print(f"  Error {error.error_code}: {error.error_description}")

    # Evaluar si los datos son utilizables
    if response.reactive_energy.code is None:
        print("Los datos principales están disponibles a pesar de errores")
    else:
        print("Error crítico - datos no disponibles")

Análisis robusto con manejo de errores:

def analyze_reactive_response(response: ReactiveResponse):
    \"\"\"Analiza respuesta de energía reactiva con manejo robusto de errores.\"\"\"

    # Verificar estado general
    print(f"Estado de la respuesta:")
    print(f"- Errores de distribuidor: {len(response.distributor_error)}")
    print(f"- Datos principales disponibles: {response.reactive_energy.code is None}")

    # Procesar errores si existen
    if response.distributor_error:
        error_types = {}
        for error in response.distributor_error:
            error_types[error.error_code] = error_types.get(error.error_code, 0) + 1

        print("Tipos de errores encontrados:")
        for error_code, count in error_types.items():
            print(f"- {error_code}: {count} distribuidor(es)")

    # Analizar datos disponibles
    if response.reactive_energy.code is None and response.reactive_energy.energy:
        print(f"✅ Análisis de {len(response.reactive_energy.energy)} períodos")

        total_periods = len(response.reactive_energy.energy)
        periods_with_data = sum(1 for p in response.reactive_energy.energy
                              if any([p.energy_p1, p.energy_p2, p.energy_p3,
                                    p.energy_p4, p.energy_p5, p.energy_p6]))

        completeness = (periods_with_data / total_periods) * 100
        print(f"Completitud de datos: {completeness:.1f}%")

        return True  # Datos utilizables
    else:
        print(f"❌ Datos no disponibles: {response.reactive_energy.code_desc}")
        return False  # Datos no utilizables

# Usar la función de análisis
data_available = analyze_reactive_response(response)
if data_available:
    # Continuar con el análisis de eficiencia energética
    pass

Comparación de respuestas V1 vs V2:

# V1: Sin información de errores específicos
# Si falla, toda la respuesta falla sin detalles

# V2: Información granular de errores
response_v2 = client.get_reactive_data(...)

print("Información detallada V2:")
print(f"- Datos obtenidos: {response_v2.reactive_energy.code is None}")
print(f"- Errores por distribuidor: {len(response_v2.distributor_error)}")

# Decidir estrategia basada en errores específicos
if response_v2.distributor_error:
    for error in response_v2.distributor_error:
        if "TIMEOUT" in error.error_code:
            print(f"Reintento recomendado para {error.distributor_name}")
        elif "NO_DATA" in error.error_code:
            print(f"Sin datos históricos en {error.distributor_name}")
Parámetros:
  • reactive_energy (ReactiveEnergyData) – Objeto ReactiveEnergyData con toda la información de energía reactiva del punto de suministro consultado

  • distributor_error (List[DistributorError]) – Lista de errores específicos por distribuidor eléctrico. Incluye información detallada sobre problemas encontrados durante la consulta a cada distribuidor involucrado

  • reactiveEnergy (ReactiveEnergyData)

  • distributorError (List[DistributorError])

Muestra:

ValidationError – Si algún componente de la respuesta no es válido

Nota

Una respuesta puede tener datos válidos en reactive_energy incluso si distributor_error contiene elementos. Evalúe ambos campos independientemente.

Truco

Para aplicaciones críticas, implemente lógica de reintento basada en los códigos de error específicos de cada distribuidor.

Ver también

  • ReactiveEnergyData - Datos principales de energía reactiva

  • DistributorError - Información detallada de errores por distribuidor

  • SimpleDatadisClientV2.get_reactive_data() - Método que retorna este modelo

Added in version 2.0: Estructura de respuesta robusta con manejo granular de errores por distribuidor

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].

reactive_energy: ReactiveEnergyData
distributor_error: List[DistributorError]
class datadis_python.models.SupplyData(*, address, cups, postalCode, province, municipality, distributor, validDateFrom, validDateTo=None, pointType, distributorCode)[fuente]

Bases: BaseModel

Modelo 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_type determina 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_code para 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 datos

  • SimpleDatadisClientV1.get_supplies() - Obtener puntos de suministro V1

  • SimpleDatadisClientV2.get_supplies() - Obtener puntos de suministro V2

  • Có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].

address: str
cups: str
postal_code: str
province: str
municipality: str
distributor: str
valid_date_from: str
valid_date_to: str | None
point_type: int
distributor_code: str
class datadis_python.models.SuppliesResponse(*, supplies=<factory>, distributorError=<factory>)[fuente]

Bases: BaseModel

Modelo Pydantic para respuesta estructurada del endpoint get-supplies V2.

Respuesta completa y robusta del endpoint get_supplies de la API V2 que incluye tanto los datos de puntos de suministro como información detallada sobre errores específicos por distribuidor. Esta estructura permite obtener datos parciales incluso cuando algunos distribuidores experimentan problemas.

Ventajas de la estructura V2:
  • Datos resilientes: Obtención de suministros incluso con errores parciales

  • Diagnóstico detallado: Información específica sobre problemas por distribuidor

  • Transparencia total: Visibilidad completa del estado de cada consulta

  • Manejo granular: Procesamiento inteligente de respuestas mixtas

Escenarios de respuesta típicos:
  • Éxito completo: Todos los suministros obtenidos, sin errores

  • Éxito parcial: Algunos suministros obtenidos, errores en distribuidores específicos

  • Fallo parcial: Datos limitados debido a múltiples errores de distribuidor

  • Fallo total: Sin datos, errores en todos los distribuidores consultados

Ejemplo

Respuesta exitosa completa:

from datadis_python.client.v2 import SimpleDatadisClientV2

with SimpleDatadisClientV2("12345678A", "password") as client:
    response = client.get_supplies()

print(f"Suministros encontrados: {len(response.supplies)}")
print(f"Errores por distribuidor: {len(response.distributor_error)}")

if not response.distributor_error:
    print("✅ Respuesta completa sin errores")

    # Procesar todos los suministros
    for supply in response.supplies:
        print(f"CUPS: {supply.cups}")
        print(f"Dirección: {supply.address}")
        print(f"Distribuidor: {supply.distributor} (código: {supply.distributor_code})")
        print("---")
else:
    print("⚠️ Respuesta con errores parciales")

Manejo de respuesta con errores:

# Analizar errores específicos por distribuidor
if response.distributor_error:
    print("Problemas detectados:")
    for error in response.distributor_error:
        print(f"- {error.distributor_name}: {error.error_description}")

    # Determinar si los datos disponibles son útiles
    error_distributors = {error.distributor_code for error in response.distributor_error}
    available_supplies = [s for s in response.supplies
                        if s.distributor_code not in error_distributors]

    print(f"Suministros utilizables: {len(available_supplies)}")

    if available_supplies:
        print("Datos parciales disponibles para procesamiento")
    else:
        print("Sin datos utilizables - todos los distribuidores con errores")

Análisis por distribuidor:

# Agrupar suministros por distribuidor
supplies_by_distributor = {}
for supply in response.supplies:
    dist_code = supply.distributor_code
    if dist_code not in supplies_by_distributor:
        supplies_by_distributor[dist_code] = []
    supplies_by_distributor[dist_code].append(supply)

# Mostrar estadísticas
print("Distribución de suministros:")
distributor_names = {
    "1": "Viesgo", "2": "E-distribución", "3": "E-redes",
    "4": "ASEME", "5": "UFD", "6": "EOSA", "7": "CIDE", "8": "IDE"
}

for dist_code, supplies_list in supplies_by_distributor.items():
    dist_name = distributor_names.get(dist_code, f"Distribuidor {dist_code}")
    print(f"- {dist_name}: {len(supplies_list)} suministros")

# Verificar errores por distribuidor
error_codes = {error.distributor_code for error in response.distributor_error}
for dist_code in supplies_by_distributor.keys():
    if dist_code in error_codes:
        print(f"  ⚠️ {distributor_names.get(dist_code)}: Datos incompletos")

Filtrado inteligente para uso posterior:

def get_reliable_supplies(response: SuppliesResponse):
    \"\"\"Obtiene solo suministros de distribuidores sin errores.\"\"\"

    # Identificar distribuidores problemáticos
    problem_distributors = {error.distributor_code
                          for error in response.distributor_error}

    # Filtrar suministros confiables
    reliable_supplies = [
        supply for supply in response.supplies
        if supply.distributor_code not in problem_distributors
    ]

    print(f"Suministros confiables: {len(reliable_supplies)}/{len(response.supplies)}")
    return reliable_supplies

# Usar solo datos confiables para consultas posteriores
reliable_supplies = get_reliable_supplies(response)

if reliable_supplies:
    # Continuar con análisis de consumo solo para suministros confiables
    first_supply = reliable_supplies[0]
    consumption_response = client.get_consumption(
        cups=first_supply.cups,
        distributor_code=first_supply.distributor_code,
        date_from="2024/01",
        date_to="2024/12"
    )

Comparación con API V1:

# V1: Lista simple, falla completamente si hay errores
# supplies_v1 = client_v1.get_supplies()  # List[SupplyData] o excepción

# V2: Respuesta estructurada con manejo de errores granular
response_v2 = client_v2.get_supplies()  # SuppliesResponse siempre

print("Diferencias V1 vs V2:")
print(f"- V1: Todo o nada ({len(response_v2.supplies)} suministros)")
print(f"- V2: Datos + errores ({len(response_v2.distributor_error)} errores)")
print(f"- V2: Información diagnóstica disponible")
Parámetros:
  • supplies (List[SupplyData]) – Lista de objetos SupplyData validados con Pydantic. Contiene todos los puntos de suministro obtenidos exitosamente de los distribuidores que respondieron correctamente

  • distributor_error (List[DistributorError]) – Lista de errores específicos por distribuidor. Incluye información detallada sobre distribuidores que experimentaron problemas durante la consulta

  • distributorError (List[DistributorError])

Muestra:

ValidationError – Si la estructura de la respuesta no es válida

Nota

Una respuesta puede contener datos válidos en supplies incluso si distributor_error no está vacío. Evalúe ambos campos independientemente.

Truco

Para aplicaciones robustas, implemente lógica de filtrado que excluya suministros de distribuidores con errores de las consultas posteriores.

Ver también

  • SupplyData - Modelo individual de punto de suministro

  • DistributorError - Información detallada de errores por distribuidor

  • SimpleDatadisClientV2.get_supplies() - Método que retorna este modelo

Added in version 2.0: Respuesta estructurada con manejo robusto de errores por distribuidor

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].

supplies: List[SupplyData]
distributor_error: List[DistributorError]
class datadis_python.models.ContractResponse(*, contract=<factory>, distributorError=<factory>)[fuente]

Bases: BaseModel

Modelo Pydantic para respuesta estructurada del endpoint get-contract-detail V2.

Respuesta completa del endpoint get_contract_detail que incluye información contractual detallada y manejo robusto de errores por distribuidor. Proporciona datos contractuales completos incluso cuando algunos distribuidores experimentan problemas técnicos.

Información contractual incluida:
  • Datos básicos: CUPS, distribuidor, comercializadora

  • Información técnica: Tensión, potencias, tarifa de acceso

  • Autoconsumo: Configuración completa de generación renovable

  • Histórico: Períodos de propiedad, cambios de comercializadora

Ejemplo

Análisis de contratos con manejo de errores:

with SimpleDatadisClientV2("12345678A", "password") as client:
    response = client.get_contract_detail(
        cups="ES001234567890123456AB",
        distributor_code="2"
    )

if response.contract:
    contract = response.contract[0]
    print(f"Tarifa: {contract.code_fare}")
    print(f"Potencia: {contract.contracted_power_kw} kW")

    if contract.self_consumption_type_desc:
        print(f"Autoconsumo: {contract.self_consumption_type_desc}")

for error in response.distributor_error:
    print(f"Error en {error.distributor_name}: {error.error_description}")
Parámetros:
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].

contract: List[ContractData]
distributor_error: List[DistributorError]
class datadis_python.models.ConsumptionResponse(*, timeCurve=<factory>, distributorError=<factory>)[fuente]

Bases: BaseModel

Modelo Pydantic para respuesta estructurada del endpoint get-consumption-data V2.

Respuesta robusta del endpoint get_consumption que incluye datos de consumo energético detallados y manejo granular de errores por distribuidor. Permite análisis de consumo incluso con problemas parciales en algunos distribuidores.

Datos de consumo incluidos:
  • Mediciones temporales: Consumos horarios o cuarto-horarios

  • Autoconsumo: Generación, autoconsumo y excedentes (si aplica)

  • Calidad de datos: Métodos de obtención (Real/Estimada)

  • Períodos completos: Rangos de fechas solicitados

Ejemplo

Análisis de consumo con datos robustos:

response = client.get_consumption(
    cups="ES001234567890123456AB",
    distributor_code="2",
    date_from="2024/01",
    date_to="2024/03"
)

print(f"Mediciones obtenidas: {len(response.time_curve)}")

# Análisis básico
total_consumption = sum(data.consumption_kwh for data in response.time_curve)
print(f"Consumo total: {total_consumption:.2f} kWh")

# Verificar errores
if response.distributor_error:
    print("Advertencias de distribuidor detectadas")
Parámetros:
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].

time_curve: List[ConsumptionData]
distributor_error: List[DistributorError]
class datadis_python.models.MaxPowerResponse(*, maxPower=<factory>, distributorError=<factory>)[fuente]

Bases: BaseModel

Modelo Pydantic para respuesta estructurada del endpoint get-max-power V2.

Respuesta completa del endpoint get_max_power que incluye registros de potencia máxima demandada y manejo robusto de errores por distribuidor. Esencial para análisis de eficiencia y optimización de contratos eléctricos.

Datos de potencia incluidos:
  • Picos de demanda: Registros de potencia máxima por período

  • Información temporal: Fechas y horas exactas de los picos

  • Períodos tarifarios: Clasificación por franjas horarias (PUNTA/VALLE/LLANO)

  • Análisis histórico: Tendencias de consumo de potencia

Ejemplo

Optimización de contrato basada en potencias máximas:

response = client.get_max_power(
    cups="ES001234567890123456AB",
    distributor_code="2",
    date_from="2024/01",
    date_to="2024/12"
)

if response.max_power:
    max_peak = max(data.max_power for data in response.max_power)
    print(f"Pico máximo anual: {max_peak/1000:.2f} kW")

    # Recomendar potencia contratada
    recommended_power = max_peak * 1.1  # 10% de margen
    print(f"Potencia recomendada: {recommended_power/1000:.2f} kW")
Parámetros:
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].

max_power: List[MaxPowerData]
distributor_error: List[DistributorError]
class datadis_python.models.DistributorsResponse(*, distExistenceUser, distributorError=<factory>)[fuente]

Bases: BaseModel

Modelo Pydantic para respuesta estructurada del endpoint get-distributors V2.

Respuesta del endpoint get_distributors que proporciona información sobre distribuidores donde el usuario tiene suministros activos, con manejo robusto de errores específicos por distribuidor. Útil para consultas preparatorias antes de solicitar datos específicos.

Información de distribuidores incluida:
  • Existencia de usuario: Confirmación de suministros por distribuidor

  • Códigos de distribuidor: Identificadores para consultas posteriores

  • Estado por distribuidor: Información de disponibilidad de datos

Ejemplo

Identificación de distribuidores disponibles:

response = client.get_distributors()

print("Distribuidores con datos disponibles:")
for dist_code, exists in response.dist_existence_user.items():
    if exists:
        distributor_names = {
            "1": "Viesgo", "2": "E-distribución", "3": "E-redes",
            "4": "ASEME", "5": "UFD", "6": "EOSA", "7": "CIDE", "8": "IDE"
        }
        name = distributor_names.get(dist_code, f"Distribuidor {dist_code}")
        print(f"- {name} (código: {dist_code})")

# Verificar errores específicos
for error in response.distributor_error:
    print(f"Problema con {error.distributor_name}: {error.error_description}")
Parámetros:
  • dist_existence_user (dict) – Diccionario con códigos de distribuidor como claves y valores boolean indicando existencia de datos del usuario

  • distributor_error (List[DistributorError]) – Lista de errores específicos por distribuidor

  • distExistenceUser (dict)

  • distributorError (List[DistributorError])

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].

dist_existence_user: dict
distributor_error: List[DistributorError]
class datadis_python.models.DistributorError(*, distributorCode, distributorName, errorCode, errorDescription)[fuente]

Bases: BaseModel

Modelo Pydantic para errores específicos por distribuidor en respuestas de API V2.

Representa información detallada sobre errores o problemas experimentados por distribuidores específicos durante la consulta a sus sistemas. Esta funcionalidad es exclusiva de la API V2 y permite un manejo granular de errores, proporcionando transparencia sobre qué distribuidores tienen problemas y cuáles funcionan correctamente.

Ventajas del manejo granular de errores:
  • Transparencia total: Identificación específica del distribuidor problemático

  • Diagnóstico preciso: Códigos y descripciones específicas para cada error

  • Resiliencia: Respuestas parcialmente exitosas cuando solo algunos distribuidores fallan

  • Debugging: Información detallada para resolución de problemas técnicos

Tipos de errores comunes:
  • Timeouts: Distribuidor no responde en tiempo límite

  • No data: Sin datos disponibles para el período solicitado

  • Auth errors: Problemas de autenticación con el distribuidor

  • System errors: Errores internos del sistema del distribuidor

  • Maintenance: Distribuidor en mantenimiento programado

Códigos de distribuidor españoles:
  • «1»: Viesgo (Cantabria, Asturias)

  • «2»: E-distribución/Endesa (Nacional)

  • «3»: E-redes (Galicia)

  • «4»: ASEME (Melilla)

  • «5»: UFD/Naturgy (Nacional)

  • «6»: EOSA (Aragón)

  • «7»: CIDE (Ceuta)

  • «8»: IDE (Baleares)

Ejemplo

Interpretación de errores por distribuidor:

from datadis_python.models.responses import DistributorError

# Error típico de timeout
timeout_error = DistributorError(
    distributorCode="2",
    distributorName="E-distribución",
    errorCode="TIMEOUT_001",
    errorDescription="Timeout al consultar datos de consumo - servidor no responde"
)

print(f"Distribuidor: {timeout_error.distributor_name}")
print(f"Código: {timeout_error.distributor_code}")
print(f"Error: {timeout_error.error_code}")
print(f"Descripción: {timeout_error.error_description}")

Casos de uso típicos:

# Verificar si hay errores específicos
if distributor_errors:
    problematic_distributors = [err.distributor_name for err in distributor_errors]
    print(f"Distribuidores con problemas: {', '.join(problematic_distributors)}")

    # Categorizar por tipo de error
    timeout_errors = [err for err in distributor_errors if "TIMEOUT" in err.error_code]
    data_errors = [err for err in distributor_errors if "NO_DATA" in err.error_code]

    if timeout_errors:
        print(f"Timeouts detectados en: {len(timeout_errors)} distribuidores")
    if data_errors:
        print(f"Sin datos disponibles en: {len(data_errors)} distribuidores")
Parámetros:
  • distributorCode (str)

  • distributorName (str)

  • errorCode (str)

  • errorDescription (str)

distributor_code

Código único del distribuidor en formato string.

Type:

str

distributor_name

Nombre comercial completo del distribuidor.

Type:

str

error_code

Código específico del error para categorización técnica.

Type:

str

error_description

Descripción detallada y legible del problema.

Type:

str

Nota

  • Los errores por distribuidor no impiden respuestas parcialmente exitosas

  • Un error en un distribuidor no afecta a los datos de otros distribuidores

  • La presencia de DistributorError no implica fallo total de la operación

  • Los códigos de error permiten implementar retry logic específico

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].

distributor_code: str
distributor_name: str
error_code: str
error_description: str