MiroxMirox
  • Plataforma

    • Filosofía
    • Visión general de la plataforma
    • Recursos de la plataforma
    • Disponibilidad y ventanas de mantenimiento
    • Releases y versiones
  • Mirox-Cloud

    • Visión general de la nube
    • Microservicios conectados
  • Mirox-Agent

    • Visión general del agente
    • Opciones de despliegue
    • Data Scraper
    • Gemelo digital
  • Detalles técnicos

    • Recopilación de métricas
  • Información

    • Plantas compatibles
  • Tipos de planta

    • Plantas solares
    • Seguidores solares y controladores de planta
    • Parques eólicos
    • Almacenamiento por baterías
    • Sistema de alarma
  • Monitorización y visualización

    • Monitorización en tiempo real
    • Gemelo digital
    • Estados de componentes
    • Códigos de estado del inversor
    • Eventos del inversor
    • Detección de pérdidas
    • Límites de potencia y recorte
    • Detección de eficiencia
    • Panel de KPI
  • Gestión de datos

    • Eventos
    • Niveles de alarma
    • Tickets
    • Previsiones
    • Informes
    • Métricas
  • Alert Manager

    • Resumen
    • Reglas de alerta
    • Alertas y notificaciones
    • Ejemplos
  • Integración y colaboración

    • Cooperaciones
    • Tokens de API
    • VPN
    • Conflictos de direcciones VPN
    • Proxy
  • IA

    • Asistente de IA y asistentes
    • Acceso agéntico (MCP)
  • Facturación

    • Mercado y tarifas
    • Cómo se calculan los ingresos
    • Contabilidad y facturación
    • Contratos
  • Colaboración

    • Invitaciones
  • Seguridad

    • Autenticación
    • Bloqueo de cuenta
    • Política de seguridad de la organización
    • Sistema de permisos
    • Segmentación de red
    • Restricciones de cooperación
    • Registro de auditoría de acceso
    • Actividad y auditoría
    • Aislamiento de datos
  • Primeros pasos

    • Onboarding
    • Configuración inicial
  • Personal

    • Usar la VPN
    • Usar el proxy
    • Autenticación de dos factores
    • Sesiones
    • Tokens de API
    • Notificaciones
    • Conectar Microsoft Teams
  • Por planta

    • Contactos
    • Dispositivos de red
    • Registradores de datos
    • Lector Modbus genérico
    • Componentes
    • VPN directa (por agente)
    • Volumen de datos
    • Importar historial
  • Organización

    • Permisos de miembros
    • Política de seguridad
    • Cooperaciones
    • Almacenamiento de archivos
    • Servicios VPN
    • Trabajar con informes
  • Exportación de datos

    • Metric Export
    • API de Metric Export
    • Migrar a Metric Export
    • API de exportación heredada
    • Generación externa de informes
    • Grafana
    • Dashboards de Grafana
    • Visión general de la API
    • Fórmulas MiroxQL (API)
  • Soporte

    • Solicitar una integración
  • Generación de informes

    • Generador de informes externo
    • Metric Export para Excel
  • Acceso remoto
  • Seguridad de la cuenta
  • IA en Mirox
  • Importación del histórico
  • Informes
  • Exportación de datos
  • English
  • Deutsch
  • Español
  • Français
  • Português
  • Italiano
  • English
  • Plataforma

    • Filosofía
    • Visión general de la plataforma
    • Recursos de la plataforma
    • Disponibilidad y ventanas de mantenimiento
    • Releases y versiones
  • Mirox-Cloud

    • Visión general de la nube
    • Microservicios conectados
  • Mirox-Agent

    • Visión general del agente
    • Opciones de despliegue
    • Data Scraper
    • Gemelo digital
  • Detalles técnicos

    • Recopilación de métricas
  • Información

    • Plantas compatibles
  • Tipos de planta

    • Plantas solares
    • Seguidores solares y controladores de planta
    • Parques eólicos
    • Almacenamiento por baterías
    • Sistema de alarma
  • Monitorización y visualización

    • Monitorización en tiempo real
    • Gemelo digital
    • Estados de componentes
    • Códigos de estado del inversor
    • Eventos del inversor
    • Detección de pérdidas
    • Límites de potencia y recorte
    • Detección de eficiencia
    • Panel de KPI
  • Gestión de datos

    • Eventos
    • Niveles de alarma
    • Tickets
    • Previsiones
    • Informes
    • Métricas
  • Alert Manager

    • Resumen
    • Reglas de alerta
    • Alertas y notificaciones
    • Ejemplos
  • Integración y colaboración

    • Cooperaciones
    • Tokens de API
    • VPN
    • Conflictos de direcciones VPN
    • Proxy
  • IA

    • Asistente de IA y asistentes
    • Acceso agéntico (MCP)
  • Facturación

    • Mercado y tarifas
    • Cómo se calculan los ingresos
    • Contabilidad y facturación
    • Contratos
  • Colaboración

    • Invitaciones
  • Seguridad

    • Autenticación
    • Bloqueo de cuenta
    • Política de seguridad de la organización
    • Sistema de permisos
    • Segmentación de red
    • Restricciones de cooperación
    • Registro de auditoría de acceso
    • Actividad y auditoría
    • Aislamiento de datos
  • Primeros pasos

    • Onboarding
    • Configuración inicial
  • Personal

    • Usar la VPN
    • Usar el proxy
    • Autenticación de dos factores
    • Sesiones
    • Tokens de API
    • Notificaciones
    • Conectar Microsoft Teams
  • Por planta

    • Contactos
    • Dispositivos de red
    • Registradores de datos
    • Lector Modbus genérico
    • Componentes
    • VPN directa (por agente)
    • Volumen de datos
    • Importar historial
  • Organización

    • Permisos de miembros
    • Política de seguridad
    • Cooperaciones
    • Almacenamiento de archivos
    • Servicios VPN
    • Trabajar con informes
  • Exportación de datos

    • Metric Export
    • API de Metric Export
    • Migrar a Metric Export
    • API de exportación heredada
    • Generación externa de informes
    • Grafana
    • Dashboards de Grafana
    • Visión general de la API
    • Fórmulas MiroxQL (API)
  • Soporte

    • Solicitar una integración
  • Generación de informes

    • Generador de informes externo
    • Metric Export para Excel
  • Acceso remoto
  • Seguridad de la cuenta
  • IA en Mirox
  • Importación del histórico
  • Informes
  • Exportación de datos
  • English
  • Deutsch
  • Español
  • Français
  • Português
  • Italiano
  • English
  • Generación de informes

    • Generador de informes externos
    • Metric Export para Excel

Generador de informes externos

Este ejemplo obtiene de Mirox los datos de una planta y crea con ellos un informe propio: primero las tres solicitudes con curl, y después un generador de informes en Python completo que las convierte en un archivo PDF.

¿Buscas trabajar con hojas de cálculo?

Para Excel —curvas de producción, varias plantas una junto a otra, inversores, sensores o aerogeneradores individuales— consulta Metric Export para Excel.

Requisitos previos

  1. Una cuenta de Mirox que pueda leer la planta.
  2. El UID de la planta: el identificador único del recurso de planta sobre el que quieres informar.
  3. Un token de API del grupo Metric Export: consulta Uso de tokens de API.

Las tres solicitudes

DatosSolicitudRespuesta
Información de la plantaGET /v1/export/report/{park_uid}/infoJSON
EventosGET /v1/export/report/{park_uid}/events.csvCSV
Cifras de producciónGET /v1/export/series/template/{template_uid}CSV, o JSON con format=json

Todas las rutas siguen a https://service.mirox.io/api. Los parámetros de la tercera solicitud se describen en la API de Metric Export; los de las dos primeras, en la API de exportación heredada.

Ejemplo 1: información de la planta

curl "https://service.mirox.io/api/v1/export/report/ABC123DEF456/info" \
  -H "Authorization: Bearer YOUR_API_TOKEN"
{
  "uid": "ABC123DEF456",
  "name": "Sunnyside Solar Park",
  "type": "solar",
  "description": "Sunnyside Solar Park is a 42 MW photovoltaic installation...",
  "latitude": 48.7511,
  "longitude": 9.1225,
  "timezone": "Europe/Berlin",
  "peak_production_w": 42000000,
  "active": true,
  "created_at": "2024-05-16T10:30:25.104830Z",
  "portfolio": {
    "uid": "GHI789JKL012",
    "name": "Renewable South"
  },
  "organization": {
    "uid": "MNO345PQR678",
    "name": "GreenPower Inc."
  },
  "address": {
    "address1": "123 Solar Way",
    "zip_code": "70123",
    "city": "Sunnyville",
    "country": "Germany"
  }
}

Ejemplo 2: eventos

curl "https://service.mirox.io/api/v1/export/report/ABC123DEF456/events.csv?year=2026&quarter=1&separator_csv=,&separator_decimal=." \
  -H "Authorization: Bearer YOUR_API_TOKEN"
Event ID,Start Date,End Date,Duration (hours),Type,Creator,Description,Priority
9876543210123456789,2026-01-15 08:45,Ongoing,720.25,Sensor Error,System,Irradiation sensor malfunction detected,1200
8765432101234567890,2026-02-10 14:30,2026-02-15 09:15,114.75,Data Availability,System,Connection issues with data acquisition system,1100
  • year, quarter y month seleccionan el periodo. Sin ellos, la exportación cubre el año en curso.
  • Sin los dos parámetros de separador, las columnas se separan con ; y el separador decimal es ,.
  • La fecha de fin de un evento que sigue abierto es Ongoing.

Filtra los eventos por prioridad

Una Priority de 1000 o más marca un evento importante, como una parada que cuesta producción o una alarma. Los valores más bajos son informativos, como una conexión que se ha restablecido.

Ejemplo 3: cifras de producción mensuales

La plantilla del sistema Informe técnico (ABCD12340002) para el primer trimestre, por mes, con ids como encabezados de columna:

curl --compressed "https://service.mirox.io/api/v1/export/series/template/ABCD12340002?park=ABC123DEF456&year=2026&quarter=1&step=month&header=id&separator_csv=,&separator_decimal=." \
  -H "Authorization: Bearer YOUR_API_TOKEN"
timestamp,plant.grid_feedin,plant.rad_pyranometer,plant.rad_weather,availability.sunhours,availability.inverter_park,plant.shutdown_grid,plant.shutdown_external,plant.expected_weather,plant.expected_pyranometer,plant.pr_weather,plant.pr_pyranometer,plant.specific_yield
2026-01,950000.4,18.4,18.1,62.5,99.1,15000.5,25000.8,980000.0,975000.0,81.2,82.0,22.6
2026-02,1250000.3,22.8,22.5,96.0,99.4,2000.3,18500.4,1300000.0,1290000.0,81.0,81.6,29.8
2026-03,1850000.8,35.4,35.0,148.3,98.9,45000.2,30000.7,1900000.0,1885000.0,80.7,81.3,44.0
  • header=id da encabezados de columna que nunca cambian. Con header=name, el valor predeterminado, los encabezados son los nombres de las métricas en el idioma que pidas, como Grid feed-in (kWh).
  • Una celda vacía significa que no hay valor. No significa cero.
  • Varias plantas o un portfolio entero van en una sola solicitud: park=ABC123DEF456,DEF456ABC123 o portfolio=<uid>, con multi_park_agg=split para una columna por planta.
  • Las columnas de la plantilla se enumeran en la guía de migración.

Muchas plantas en un periodo largo

Una planta durante un año entero cabe en una sola solicitud, y también 25 plantas. Una solicitud demasiado grande responde 400 over_budget y nombra el periodo más largo que cabe. Solicita entonces un trimestre cada vez, o un portfolio cada vez, y une los archivos. Consulta Dividir una exportación grande.

La plantilla anterior

Los informes basados en la plantilla anterior Report Technical v1 siguen funcionando. Su enlace y sus columnas no han cambiado:

curl "https://service.mirox.io/api/v1/export/metrics/template/ABCD12340001?park=ABC123DEF456&resolution=monthly" \
  -H "Authorization: Bearer YOUR_API_TOKEN"

En qué se diferencia la sucesora ABCD12340002:

ABCD12340001 (heredada)ABCD12340002
Enlace/v1/export/metrics/template/…/v1/export/series/template/…
Encabezados de columnalos nombres de la exportación anterior, como Energy Production (kWh)los nombres de la colección de métricas, como Grid feed-in (kWh), o ids con header=id
Primera columnaDate (YYYY-MM)Date, o timestamp con header=id
Columna adicionalDays in Month (d) con resolución mensualninguna
Valor ausente0vacío
Días y mesesUTCla hora local de la planta
Columnas calculadasincluidasel performance ratio, la producción esperada y la producción específica están incluidos; las demás se omiten

Usar las solicitudes en otras herramientas

  • Python: la biblioteca requests, como en el script de más abajo.
  • Excel: Power Query con el encabezado Authorization: consulta Excel con Power Query.
  • Power BI: la misma conexión de Power Query que en Excel.
  • Tableau y otras: cualquier herramienta que lea un enlace CSV o JSON y pueda enviar un encabezado.

Solución de problemas

RespuestaSignificadoQué hacer
401El token falta, ha caducado o se ha revocado.Crea un token nuevo, o rota el existente.
403 no_accessible_parksNo puedes leer las mediciones de la planta.Comprueba el UID y tu rol en la planta.
400 over_budgetLa exportación es demasiado grande.Usa el paso o el periodo que la respuesta nombra en hint, o divide la exportación.
400 step_not_servedLa plantilla no tiene valor con este paso.Usa uno de los pasos de hint.offered_common.
429Demasiadas exportaciones a la vez, o el cupo por hora está agotado.Espera los segundos del encabezado Retry-After.
Los caracteres especiales se ven malLos archivos son UTF-8.Indica la codificación al importar el archivo.

Ejemplo en Python: generador de informes SolarViz

El script obtiene la información de la planta, los eventos importantes del trimestre en curso y las cifras mensuales del año hasta la fecha, y crea un PDF con un resumen, un gráfico y dos tablas.

Requisitos previos

pip install requests pandas matplotlib reportlab
  • requests: las solicitudes a Mirox
  • pandas: leer y sumar los datos CSV
  • matplotlib: el gráfico
  • reportlab: el archivo PDF

El script lee el token de la variable de entorno MIROX_TOKEN:

export MIROX_TOKEN="YOUR_API_TOKEN"
python external_report_generator.py

Código de ejemplo completo

También puedes descargar el script.

#!/usr/bin/env python3
"""
SolarViz - Mirox External Report Generator

Fetches the data of one plant from the Mirox API and builds an energy
production report with a chart as a PDF file:

  * plant information          GET /api/v1/export/report/{plant}/info
  * events                     GET /api/v1/export/report/{plant}/events.csv
  * monthly production figures GET /api/v1/export/series/template/{template}

Requires: pip install requests pandas matplotlib reportlab
The API token is read from the environment variable MIROX_TOKEN.
"""

import io
import os
import sys
import time
from datetime import datetime, timezone

import matplotlib

matplotlib.use("Agg")
import matplotlib.pyplot as plt
import pandas as pd
import requests
from reportlab.lib import colors
from reportlab.lib.pagesizes import A4
from reportlab.lib.styles import getSampleStyleSheet
from reportlab.lib.units import cm
from reportlab.platypus import Image, Paragraph, SimpleDocTemplate, Spacer, Table, TableStyle

# Configuration - replace with your values
API_URL = "https://service.mirox.io"
API_TOKEN = os.environ.get("MIROX_TOKEN", "YOUR_API_TOKEN")  # group "Metric Export"
PARK_UID = "ABC123DEF456"  # the UID of the plant
TEMPLATE_UID = "ABCD12340002"  # system template "Report Technical"

# The report covers this year so far; the events are those of the current quarter
YEAR = datetime.now(timezone.utc).year
QUARTER = (datetime.now(timezone.utc).month - 1) // 3 + 1

HEADERS = {"Authorization": f"Bearer {API_TOKEN}", "Accept-Encoding": "gzip"}

# One CSV dialect for every request, independent of regional settings
CSV_FORMAT = {"separator_csv": ",", "separator_decimal": "."}

# The columns the report reads, by metric id (header=id)
PRODUCTION = "plant.grid_feedin"
EXPECTED = "plant.expected_weather"
LOSS_GRID = "plant.shutdown_grid"
LOSS_MARKETER = "plant.shutdown_external"
PERFORMANCE_RATIO = "plant.pr_weather"


class ExportRefused(Exception):
    """A coded refusal of the Metric Export: what was wrong, and the way out."""

    def __init__(self, status, detail):
        self.status = status
        self.code = detail.get("code", "")
        self.params = detail.get("params", {})
        self.hint = detail.get("hint", {})
        super().__init__(f"{status} {self.code}: {detail.get('message', '')} "
                         f"params={self.params} hint={self.hint}")


def get(path, params=None, retries=3):
    """GET with the token. Waits and repeats when the export is busy (429)."""
    for _attempt in range(retries):
        response = requests.get(f"{API_URL}{path}", headers=HEADERS, params=params, timeout=90)
        if response.status_code == 429:
            time.sleep(int(response.headers.get("Retry-After", "5")))
            continue
        if response.status_code >= 400:
            try:
                detail = response.json().get("detail")
            except ValueError:
                detail = None
            if isinstance(detail, dict):
                raise ExportRefused(response.status_code, detail)
            response.raise_for_status()
        return response
    raise RuntimeError(f"{path}: still busy after {retries} attempts")


def fetch_park_info():
    """Plant information as a dictionary."""
    return get(f"/api/v1/export/report/{PARK_UID}/info").json()


def fetch_events(year, quarter):
    """The important events of a quarter as a DataFrame."""
    response = get(f"/api/v1/export/report/{PARK_UID}/events.csv",
                   {"year": year, "quarter": quarter, **CSV_FORMAT})
    events = pd.read_csv(io.StringIO(response.content.decode("utf-8")))
    if events.empty:
        return events
    # A priority of 1000 or more marks an important event
    events = events[events["Priority"] >= 1000]
    return events.drop(columns=["Event ID", "Priority"], errors="ignore")


def fetch_metrics(year):
    """Monthly figures of the template for the year so far.

    header=id gives column names that never change. A year in progress ends
    now; the notes of the answer say so.
    """
    response = get(f"/api/v1/export/series/template/{TEMPLATE_UID}", {
        "park": PARK_UID, "year": year, "step": "month", "header": "id", **CSV_FORMAT})
    for note in filter(None, response.headers.get("X-Mirox-Notes", "").split(";")):
        print(f"  note: {note}")
    metrics = pd.read_csv(io.StringIO(response.content.decode("utf-8")))
    # An empty cell means "no data"; it is read as NaN and left out of the sums
    return metrics.rename(columns={"timestamp": "month"})


def create_production_chart(metrics):
    """Monthly production with the shutdown losses stacked, against the expectation."""
    plt.figure(figsize=(8, 4))
    positions = range(len(metrics))
    production = metrics[PRODUCTION].fillna(0)
    loss_grid = metrics[LOSS_GRID].fillna(0)
    loss_marketer = metrics[LOSS_MARKETER].fillna(0)

    plt.bar(positions, production, color="#2986cc", label="Grid feed-in")
    plt.bar(positions, loss_grid, bottom=production, color="#e06666",
            label="Shutdown loss, grid operator")
    plt.bar(positions, loss_marketer, bottom=production + loss_grid, color="#f1c232",
            label="Shutdown loss, direct marketer")
    plt.plot(positions, metrics[EXPECTED], marker="o", color="#6aa84f", linewidth=2,
             label="Expected production")

    plt.title("Monthly production and shutdown losses")
    plt.xlabel("Month")
    plt.ylabel("Energy (kWh)")
    plt.xticks(positions, metrics["month"], rotation=45)
    plt.grid(axis="y", alpha=0.3)
    plt.legend(loc="upper left")
    plt.tight_layout()

    buffer = io.BytesIO()
    plt.savefig(buffer, format="png", dpi=300)
    buffer.seek(0)
    plt.close()
    return buffer


def calculate_performance(metrics):
    """The figures of the summary table."""
    production = metrics[PRODUCTION].sum()
    expected = metrics[EXPECTED].sum()
    loss_grid = metrics[LOSS_GRID].sum()
    loss_marketer = metrics[LOSS_MARKETER].sum()
    losses = loss_grid + loss_marketer
    return {
        "production": production,
        "expected": expected,
        "production_vs_expected": production / expected * 100 if expected else None,
        "loss_grid": loss_grid,
        "loss_marketer": loss_marketer,
        "losses": losses,
        "loss_share": losses / (production + losses) * 100 if production + losses else None,
    }


def number(value, unit="", digits=2):
    if value is None or pd.isna(value):
        return "-"
    return f"{value:,.{digits}f}{unit}"


def plain_table(rows, widths):
    table = Table(rows, colWidths=widths)
    table.setStyle(TableStyle([
        ("FONTNAME", (0, 0), (0, -1), "Helvetica-Bold"),
        ("VALIGN", (0, 0), (-1, -1), "MIDDLE"),
        ("TOPPADDING", (0, 0), (-1, -1), 3),
        ("BOTTOMPADDING", (0, 0), (-1, -1), 3),
    ]))
    return table


def grid_table(rows, font_size=9):
    table = Table(rows, repeatRows=1)
    table.setStyle(TableStyle([
        ("BACKGROUND", (0, 0), (-1, 0), colors.lightgrey),
        ("FONTNAME", (0, 0), (-1, 0), "Helvetica-Bold"),
        ("ALIGN", (0, 0), (-1, 0), "CENTER"),
        ("ALIGN", (1, 1), (-1, -1), "RIGHT"),
        ("GRID", (0, 0), (-1, -1), 0.5, colors.grey),
        ("VALIGN", (0, 0), (-1, -1), "MIDDLE"),
        ("FONTSIZE", (0, 0), (-1, -1), font_size),
    ]))
    return table


def generate_pdf_report(park_info, events, metrics, performance, chart):
    """Build the PDF and return its file name."""
    output_file = f"SolarViz_Report_{park_info['name'].replace(' ', '_')}_{YEAR}_Q{QUARTER}.pdf"
    doc = SimpleDocTemplate(output_file, pagesize=A4)
    styles = getSampleStyleSheet()
    styles["Title"].fontSize = 18
    address = park_info.get("address") or {}
    portfolio = park_info.get("portfolio") or {}
    organization = park_info.get("organization") or {}

    elements = [
        Paragraph(f"Energy Production Report: {park_info['name']}", styles["Title"]),
        Spacer(1, 0.5 * cm),
        Paragraph("Plant Information", styles["Heading2"]),
        plain_table([
            ["Name:", park_info["name"]],
            ["Type:", park_info.get("type", "")],
            ["Location:", f"{address.get('city', '')}, {address.get('country', '')}"],
            ["Peak power:", number((park_info.get("peak_production_w") or 0) / 1e6, " MW")],
            ["Portfolio:", portfolio.get("name", "")],
            ["Organization:", organization.get("name", "")],
        ], [3 * cm, 10 * cm]),
        Spacer(1, 0.5 * cm),
        Paragraph(f"Summary ({YEAR} to date)", styles["Heading2"]),
        plain_table([
            ["Grid feed-in:", number(performance["production"], " kWh")],
            ["Expected production:", number(performance["expected"], " kWh")],
            ["Feed-in against expectation:", number(performance["production_vs_expected"], " %")],
            ["Shutdown losses:", number(performance["losses"], " kWh")],
            ["  by the grid operator:", number(performance["loss_grid"], " kWh")],
            ["  by the direct marketer:", number(performance["loss_marketer"], " kWh")],
            ["Share of the losses:", number(performance["loss_share"], " %")],
        ], [6 * cm, 7 * cm]),
        Spacer(1, 0.5 * cm),
        Paragraph("Monthly Production", styles["Heading2"]),
        Image(chart, width=16 * cm, height=8 * cm),
        Spacer(1, 0.5 * cm),
    ]

    rows = [["Month", "Feed-in (kWh)", "Expected (kWh)", "Loss grid (kWh)",
             "Loss marketer (kWh)", "PR (%)"]]
    for _, row in metrics.iterrows():
        rows.append([row["month"], number(row[PRODUCTION]), number(row[EXPECTED]),
                     number(row[LOSS_GRID]), number(row[LOSS_MARKETER]),
                     number(row[PERFORMANCE_RATIO], digits=1)])
    elements += [grid_table(rows), Spacer(1, 0.5 * cm),
                 Paragraph("Important Events", styles["Heading2"])]

    if events.empty:
        elements.append(Paragraph("No important events in this quarter.", styles["Normal"]))
    else:
        elements.append(grid_table([events.columns.tolist()] + events.values.tolist(), 8))

    elements += [
        Spacer(1, 1 * cm),
        Paragraph("Data Source", styles["Heading2"]),
        Paragraph(
            "Data exported from the Mirox platform through the Metric Export API.<br/>"
            f"Created: {datetime.now(timezone.utc).strftime('%Y-%m-%d %H:%M')} UTC<br/><br/>"
            "<i>Generated by SolarViz - External Reporting Tool</i>", styles["Normal"]),
    ]
    doc.build(elements)
    return output_file


def main():
    try:
        print("Fetching plant information...")
        park_info = fetch_park_info()

        print("Fetching events...")
        events = fetch_events(YEAR, QUARTER)

        print("Fetching monthly figures...")
        metrics = fetch_metrics(YEAR)

        print("Building the report...")
        performance = calculate_performance(metrics)
        chart = create_production_chart(metrics)
        output_file = generate_pdf_report(park_info, events, metrics, performance, chart)
        print(f"Report created: {output_file}")
    except ExportRefused as refused:
        print(f"The export was refused: {refused}")
        if refused.code == "over_budget":
            print("Use a coarser step or a shorter range, see hint.step and hint.max_days.")
        sys.exit(1)
    except requests.RequestException as error:
        print(f"The request failed: {error}")
        sys.exit(1)


if __name__ == "__main__":
    main()

Cómo funciona

  1. Configuración. La dirección de Mirox, la planta y la plantilla están al principio. El token viene del entorno, así que nunca acaba en un archivo que compartas.
  2. Una función para todas las solicitudes. get envía el token, espera y repite cuando Mirox responde 429, y convierte un rechazo de la exportación en un error que lleva code, params y hint.
  3. Cifras mensuales. fetch_metrics pide la plantilla para todo el año con step=month y header=id, e imprime las notas de la respuesta. En un año que aún está en curso, el periodo termina ahora.
  4. Cifras. Las celdas vacías se leen como «sin datos» y se dejan fuera de las sumas.
  5. Informe. ReportLab crea el PDF: información de la planta, resumen, gráfico, tabla mensual y eventos importantes.

Personalización

  • Periodo: cambia YEAR para las cifras y QUARTER para los eventos.
  • Columnas: usa otra plantilla, o solicita métricas directamente con GET /v1/export/series y metrics=….
  • Datos más finos: step=day da filas diarias. Para curvas de 15 minutos, solicita las métricas de potencia con GET /v1/export/series.
  • Varias plantas: pasa varios UIDs en park, o un portfolio.
  • Automatización: ejecuta el script desde un planificador, con el token en el entorno de la tarea.

Funciones relacionadas

  • Metric Export para Excel: recetas y Power Query
  • Generación de informes externos: cuándo crear informes fuera de Mirox
  • API de Metric Export: la referencia de las solicitudes usadas aquí
  • Uso de tokens de API: crea y gestiona el token
  • Informes: los informes que genera el propio Mirox
Next
Metric Export para Excel
© 2026 Mirox Verwaltungs GmbH. Todos los derechos reservados. | Política de privacidad