UPDATE // RELASE NOTES Solar Forecast ML

PATCH-SCRIPT für SFML STATS “JAHRESPROGNOSE”

Es hat sich ein kleiner Fehler im letzten Update von SFML STATS eingeschlichen. Diese wird im kommenden Update behoben.
Wer es aber schon jetzt beheben möchte:

Schritt 1:
die hier angehängte Datei herunterladen

Schritt 2:
Die Dateiendung von .txt in .py umändern

Schritt 3:
zu folgenden Verzeichniss auf eurem HA navigieren (SMB, Filebrowser,…)

config/custom_components/sfml_stats/readers

Schritt 4:
Die Datei: annual_forecast_reader.py durch die heruntergeladene Datei ersetzen
(bitte vorher die Orginaldatei sichern)

Schritt 5:
HA neuladen

Viel Spaß

Zara

WICHTIGER HINWEIS IN DIESEM THREAD SCHREIBE NUR ICH ODER ADMIN`S BITTE NUTZT FÜR WEITERE FRAGEN DEN ENTSPRECHENDEN THREAD

Wer lieber mit einem Code-Editor arbeitet, bitte den Code mit diesem hier ersetzen:

# ******************************************************************************
# @copyright (C) 2026 Zara-Toorox - Solar Forecast Stats x86 DB-Version part of Solar Forecast ML DB
# * This program is protected by a Proprietary Non-Commercial License.
# 1. Personal and Educational use only.
# 2. COMMERCIAL USE AND AI TRAINING ARE STRICTLY PROHIBITED.
# 3. Clear attribution to "Zara-Toorox" is required.
# * Full license terms: https://github.com/Zara-Toorox/ha-solar-forecast-ml/blob/main/LICENSE
# ******************************************************************************

"""Annual yield forecast reader for SFML Stats. @zara"""
from __future__ import annotations

import logging
import math
from contextlib import asynccontextmanager
from dataclasses import dataclass, field
from datetime import date, datetime, timedelta
from pathlib import Path
from typing import Any, AsyncIterator, TYPE_CHECKING

import aiosqlite

from ..const import SOLAR_FORECAST_DB

if TYPE_CHECKING:
    from ..storage.db_connection_manager import DatabaseConnectionManager

_LOGGER = logging.getLogger(__name__)

# Berlin TMY clear-sky index per month (ratio of actual/clear-sky irradiation).
# Derived from DWD TRY 2015 reference year for Central European lowlands.
# Used to extrapolate months without measured data.
_TMY_CLEARSKY_INDEX = {
    1: 0.28, 2: 0.34, 3: 0.42, 4: 0.50, 5: 0.52,
    6: 0.53, 7: 0.51, 8: 0.49, 9: 0.44, 10: 0.35,
    11: 0.26, 12: 0.23,
}

# Approximate daylight hours per month at 52°N for physics baseline scaling.
_DAYLIGHT_HOURS_52N = {
    1: 8.3, 2: 9.8, 3: 11.8, 4: 13.9, 5: 15.6,
    6: 16.6, 7: 16.1, 8: 14.6, 9: 12.7, 10: 10.7,
    11: 8.8, 12: 7.9,
}


@dataclass
class MonthlyYield:
    """Monthly yield summary with measured and projected values. @zara"""

    year: int
    month: int
    measured_days: int
    total_days_in_month: int

    # Measured values (from actual data)
    measured_yield_kwh: float
    measured_avg_daily_kwh: float
    measured_min_day_kwh: float
    measured_max_day_kwh: float
    measured_avg_peak_w: float
    measured_avg_production_hours: float
    measured_avg_accuracy: float

    # Projected values (full month estimate)
    projected_yield_kwh: float
    source: str  # "measured", "partial", "estimated"

    # Self-consumption data (if available)
    self_consumption_kwh: float = 0.0
    grid_export_kwh: float = 0.0
    avg_autarky_percent: float = 0.0


@dataclass
class CalibrationData:
    """Panel group calibration state. @zara"""

    group_name: str
    global_factor: float
    sample_count: int
    confidence: float


@dataclass
class AnnualForecast:
    """Complete annual yield forecast result. @zara"""

    # System info
    latitude: float
    longitude: float
    installed_kwp: float
    panel_groups: list[CalibrationData]

    # Data coverage
    first_data_date: date
    last_data_date: date
    total_measured_days: int

    # Monthly breakdown
    months: list[MonthlyYield]

    # Aggregated annual values
    annual_yield_kwh: float
    specific_yield_kwh_per_kwp: float
    performance_ratio: float
    best_month: int
    worst_month: int

    # Confidence
    measured_months: int
    estimated_months: int
    confidence_percent: float

    # Yield bands
    optimistic_yield_kwh: float
    pessimistic_yield_kwh: float

    # Records
    record_day_kwh: float
    record_day_date: str
    record_peak_w: float


class AnnualForecastReader:
    """Reads historical data and computes an annual yield forecast. @zara"""

    _db_manager: DatabaseConnectionManager | None = None

    def __init__(
        self,
        config_path: Path,
        db_manager: DatabaseConnectionManager | None = None,
    ) -> None:
        """Initialize the annual forecast reader. @zara"""
        self._config_path = config_path
        self._db_path = config_path / SOLAR_FORECAST_DB
        if db_manager is not None:
            AnnualForecastReader._db_manager = db_manager

    @property
    def is_available(self) -> bool:
        """Check if database is accessible. @zara"""
        if self._db_manager is not None:
            return self._db_manager.is_available
        return self._db_path.exists()

    @asynccontextmanager
    async def _get_db_connection(self) -> AsyncIterator[aiosqlite.Connection]:
        """Get a database connection from the manager. @zara"""
        from ..storage.db_connection_manager import get_manager

        manager = get_manager()
        if manager is not None and manager.is_connected:
            yield await manager.get_connection()
        else:
            conn = await aiosqlite.connect(str(self._db_path))
            conn.row_factory = aiosqlite.Row
            try:
                yield conn
            finally:
                await conn.close()

    async def async_get_annual_forecast(self) -> AnnualForecast | None:
        """Compute a full annual yield forecast from historical data. @zara"""
        if not self.is_available:
            _LOGGER.debug("Database not found: %s", self._db_path)
            return None

        try:
            async with self._get_db_connection() as conn:
                system_info = await self._get_system_info(conn)
                calibration = await self._get_calibration_data(conn)
                monthly_data = await self._get_monthly_summaries(conn)
                energy_data = await self._get_monthly_energy(conn)
                records = await self._get_records(conn)
                data_range = await self._get_data_range(conn)

            if not system_info:
                _LOGGER.warning("No system info found in database")
                return None

            latitude = system_info["latitude"]
            installed_kwp = system_info["installed_kwp"]

            # Build 12-month projection
            months = self._build_monthly_projection(
                monthly_data, energy_data, installed_kwp, latitude
            )

            annual_yield = sum(m.projected_yield_kwh for m in months)
            specific_yield = annual_yield / installed_kwp if installed_kwp > 0 else 0

            # Theoretical maximum (clear sky, no losses)
            theoretical_max = self._compute_theoretical_annual(installed_kwp, latitude)
            performance_ratio = annual_yield / theoretical_max if theoretical_max > 0 else 0

            best_month = max(months, key=lambda m: m.projected_yield_kwh).month
            worst_month = min(months, key=lambda m: m.projected_yield_kwh).month

            measured_months = sum(1 for m in months if m.source == "measured")
            partial_months = sum(1 for m in months if m.source == "partial")
            estimated_months = sum(1 for m in months if m.source == "estimated")

            # Confidence based on data coverage
            confidence = (measured_months * 100 + partial_months * 60) / 12

            # Yield bands from daily variance
            optimistic, pessimistic = self._compute_yield_bands(
                months, annual_yield
            )

            return AnnualForecast(
                latitude=latitude,
                longitude=system_info["longitude"],
                installed_kwp=installed_kwp,
                panel_groups=calibration,
                first_data_date=data_range["first"],
                last_data_date=data_range["last"],
                total_measured_days=data_range["count"],
                months=months,
                annual_yield_kwh=round(annual_yield, 1),
                specific_yield_kwh_per_kwp=round(specific_yield, 0),
                performance_ratio=round(performance_ratio * 100, 1),
                best_month=best_month,
                worst_month=worst_month,
                measured_months=measured_months,
                estimated_months=estimated_months,
                confidence_percent=round(confidence, 1),
                optimistic_yield_kwh=round(optimistic, 1),
                pessimistic_yield_kwh=round(pessimistic, 1),
                record_day_kwh=records.get("max_kwh", 0),
                record_day_date=records.get("max_date", ""),
                record_peak_w=records.get("peak_w", 0),
            )

        except Exception as err:
            _LOGGER.error("Error computing annual forecast: %s", err)
            return None

    # -------------------------------------------------------------------------
    # Database queries
    # -------------------------------------------------------------------------

    async def _get_system_info(self, conn: aiosqlite.Connection) -> dict | None:
        """Read system info from astronomy_system_info. @zara"""
        try:
            async with conn.execute(
                """SELECT latitude, longitude, installed_capacity_kwp,
                          max_peak_record_kwh, max_peak_date, max_peak_hour
                   FROM astronomy_system_info WHERE id = 1"""
            ) as cursor:
                row = await cursor.fetchone()
                if row:
                    return {
                        "latitude": row["latitude"],
                        "longitude": row["longitude"],
                        "installed_kwp": row["installed_capacity_kwp"] or 0,
                    }
        except Exception as err:
            _LOGGER.debug("Error reading system info: %s", err)
        return None

    async def _get_calibration_data(
        self, conn: aiosqlite.Connection
    ) -> list[CalibrationData]:
        """Read panel group calibration factors. @zara"""
        result: list[CalibrationData] = []
        try:
            async with conn.execute(
                """SELECT group_name, global_factor, sample_count, confidence
                   FROM physics_calibration_groups"""
            ) as cursor:
                rows = await cursor.fetchall()
                for row in rows:
                    result.append(CalibrationData(
                        group_name=row["group_name"],
                        global_factor=row["global_factor"],
                        sample_count=row["sample_count"],
                        confidence=row["confidence"],
                    ))
        except Exception as err:
            _LOGGER.debug("Error reading calibration data: %s", err)
        return result

    async def _get_monthly_summaries(
        self, conn: aiosqlite.Connection
    ) -> dict[str, dict]:
        """Aggregate daily_summaries by year-month. @zara"""
        result: dict[str, dict] = {}
        try:
            async with conn.execute(
                """SELECT
                       strftime('%Y-%m', date) AS ym,
                       COUNT(*) AS days,
                       ROUND(AVG(actual_total_kwh), 3) AS avg_daily,
                       ROUND(SUM(actual_total_kwh), 3) AS total_kwh,
                       ROUND(MIN(actual_total_kwh), 3) AS min_day,
                       ROUND(MAX(actual_total_kwh), 3) AS max_day,
                       ROUND(AVG(peak_power_w), 0) AS avg_peak_w,
                       ROUND(AVG(production_hours), 1) AS avg_prod_hours,
                       ROUND(AVG(accuracy_percent), 1) AS avg_accuracy
                   FROM daily_summaries
                   WHERE actual_total_kwh > 0
                   GROUP BY strftime('%Y-%m', date)
                   ORDER BY ym"""
            ) as cursor:
                rows = await cursor.fetchall()
                for row in rows:
                    result[row["ym"]] = {
                        "days": row["days"],
                        "avg_daily": row["avg_daily"] or 0,
                        "total_kwh": row["total_kwh"] or 0,
                        "min_day": row["min_day"] or 0,
                        "max_day": row["max_day"] or 0,
                        "avg_peak_w": row["avg_peak_w"] or 0,
                        "avg_prod_hours": row["avg_prod_hours"] or 0,
                        "avg_accuracy": row["avg_accuracy"] or 0,
                    }
        except Exception as err:
            _LOGGER.debug("Error reading monthly summaries: %s", err)
        return result

    async def _get_monthly_energy(
        self, conn: aiosqlite.Connection
    ) -> dict[str, dict]:
        """Aggregate energy stats by year-month. @zara"""
        result: dict[str, dict] = {}
        try:
            async with conn.execute(
                """SELECT
                       strftime('%Y-%m', date) AS ym,
                       ROUND(SUM(self_consumption_kwh), 2) AS self_consumed,
                       ROUND(SUM(grid_export_kwh), 2) AS exported,
                       ROUND(AVG(autarkie_percent), 1) AS avg_autarky
                   FROM stats_daily_energy
                   WHERE solar_yield_kwh > 0
                   GROUP BY strftime('%Y-%m', date)
                   ORDER BY ym"""
            ) as cursor:
                rows = await cursor.fetchall()
                for row in rows:
                    result[row["ym"]] = {
                        "self_consumed": row["self_consumed"] or 0,
                        "exported": row["exported"] or 0,
                        "avg_autarky": row["avg_autarky"] or 0,
                    }
        except Exception as err:
            _LOGGER.debug("Error reading monthly energy: %s", err)
        return result

    async def _get_records(self, conn: aiosqlite.Connection) -> dict:
        """Get all-time records. @zara"""
        result: dict[str, Any] = {}
        try:
            async with conn.execute(
                """SELECT date, actual_total_kwh, peak_power_w
                   FROM daily_summaries
                   WHERE actual_total_kwh > 0
                   ORDER BY actual_total_kwh DESC
                   LIMIT 1"""
            ) as cursor:
                row = await cursor.fetchone()
                if row:
                    result["max_kwh"] = row["actual_total_kwh"]
                    result["max_date"] = row["date"]

            async with conn.execute(
                """SELECT max_peak_record_kwh FROM astronomy_system_info WHERE id = 1"""
            ) as cursor:
                row = await cursor.fetchone()
                if row:
                    result["peak_w"] = row["max_peak_record_kwh"] or 0
        except Exception as err:
            _LOGGER.debug("Error reading records: %s", err)
        return result

    async def _get_data_range(self, conn: aiosqlite.Connection) -> dict:
        """Get first/last date and total days with data. @zara"""
        try:
            async with conn.execute(
                """SELECT MIN(date) AS first_date, MAX(date) AS last_date,
                          COUNT(*) AS total_days
                   FROM daily_summaries
                   WHERE actual_total_kwh > 0"""
            ) as cursor:
                row = await cursor.fetchone()
                if row and row["first_date"]:
                    return {
                        "first": date.fromisoformat(row["first_date"]),
                        "last": date.fromisoformat(row["last_date"]),
                        "count": row["total_days"],
                    }
        except Exception as err:
            _LOGGER.debug("Error reading data range: %s", err)
        return {"first": date.today(), "last": date.today(), "count": 0}

    # -------------------------------------------------------------------------
    # Projection logic
    # -------------------------------------------------------------------------

    def _build_monthly_projection(
        self,
        monthly_data: dict[str, dict],
        energy_data: dict[str, dict],
        installed_kwp: float,
        latitude: float,
    ) -> list[MonthlyYield]:
        """Build a 12-month projection mixing measured and estimated data. @zara"""
        import calendar

        today = date.today()
        current_year = today.year
        months: list[MonthlyYield] = []

        for month_num in range(1, 13):
            days_in_month = calendar.monthrange(current_year, month_num)[1]

            # Find the best matching data for this calendar month
            measured = self._find_best_month_data(monthly_data, month_num)
            energy = self._find_best_month_energy(energy_data, month_num)

            if measured and measured["days"] >= days_in_month * 0.8:
                # Full month of data (80%+ coverage) — use as-is
                projected = measured["total_kwh"]
                # Scale to full month if slightly incomplete
                if measured["days"] < days_in_month:
                    projected = measured["avg_daily"] * days_in_month
                source = "measured"
            elif measured and measured["days"] >= 5:
                # Partial month — extrapolate from daily average
                projected = measured["avg_daily"] * days_in_month
                source = "partial"
            else:
                # No data — estimate from physics + climate
                projected = self._estimate_month(
                    month_num, installed_kwp, latitude
                )
                measured = {
                    "days": 0, "avg_daily": projected / days_in_month,
                    "total_kwh": 0, "min_day": 0, "max_day": 0,
                    "avg_peak_w": 0, "avg_prod_hours": 0, "avg_accuracy": 0,
                }
                source = "estimated"

            months.append(MonthlyYield(
                year=current_year,
                month=month_num,
                measured_days=measured["days"],
                total_days_in_month=days_in_month,
                measured_yield_kwh=round(measured["total_kwh"], 2),
                measured_avg_daily_kwh=round(measured["avg_daily"], 2),
                measured_min_day_kwh=round(measured["min_day"], 2),
                measured_max_day_kwh=round(measured["max_day"], 2),
                measured_avg_peak_w=round(measured["avg_peak_w"], 0),
                measured_avg_production_hours=round(measured["avg_prod_hours"], 1),
                measured_avg_accuracy=round(measured["avg_accuracy"], 1),
                projected_yield_kwh=round(projected, 2),
                source=source,
                self_consumption_kwh=round(energy.get("self_consumed", 0), 2) if energy else 0,
                grid_export_kwh=round(energy.get("exported", 0), 2) if energy else 0,
                avg_autarky_percent=round(energy.get("avg_autarky", 0), 1) if energy else 0,
            ))

        return months

    def _find_best_month_data(
        self, monthly_data: dict[str, dict], target_month: int
    ) -> dict | None:
        """Find the best available data for a given calendar month. @zara"""
        # Prefer most recent year with data for this month
        candidates = []
        for ym, data in monthly_data.items():
            try:
                parts = ym.split("-")
                if int(parts[1]) == target_month:
                    candidates.append((int(parts[0]), data))
            except (ValueError, IndexError):
                continue

        if not candidates:
            return None

        # Return data from the most recent year
        candidates.sort(key=lambda x: x[0], reverse=True)
        return candidates[0][1]

    def _find_best_month_energy(
        self, energy_data: dict[str, dict], target_month: int
    ) -> dict | None:
        """Find energy data for a given calendar month. @zara"""
        candidates = []
        for ym, data in energy_data.items():
            try:
                parts = ym.split("-")
                if int(parts[1]) == target_month:
                    candidates.append((int(parts[0]), data))
            except (ValueError, IndexError):
                continue

        if not candidates:
            return None

        candidates.sort(key=lambda x: x[0], reverse=True)
        return candidates[0][1]

    def _estimate_month(
        self, month: int, installed_kwp: float, latitude: float
    ) -> float:
        """Estimate monthly yield from physics baseline and climate data. @zara"""
        import calendar

        days = calendar.monthrange(date.today().year, month)[1]
        daylight = _DAYLIGHT_HOURS_52N.get(month, 12.0)

        # Adjust daylight for actual latitude (simple linear scaling)
        lat_factor = 1.0 + (latitude - 52.0) * 0.01

        # Peak sun hours = daylight * clear-sky index * atmospheric losses
        clearsky_idx = _TMY_CLEARSKY_INDEX.get(month, 0.40)
        peak_sun_hours = daylight * clearsky_idx * 0.75 * lat_factor

        # Monthly yield = kWp * PSH * days * system_efficiency
        system_efficiency = 0.82
        monthly_kwh = installed_kwp * peak_sun_hours * days * system_efficiency

        return monthly_kwh

    def _compute_theoretical_annual(
        self, installed_kwp: float, latitude: float
    ) -> float:
        """Compute theoretical annual clear-sky yield. @zara"""
        import calendar

        total = 0.0
        year = date.today().year
        for month in range(1, 13):
            days = calendar.monthrange(year, month)[1]
            daylight = _DAYLIGHT_HOURS_52N.get(month, 12.0)
            lat_factor = 1.0 + (latitude - 52.0) * 0.01
            # Clear-sky: no cloud losses, only basic atmospheric + system
            total += installed_kwp * daylight * 0.75 * lat_factor * days * 0.82
        return total

    def _compute_yield_bands(
        self, months: list[MonthlyYield], expected: float
    ) -> tuple[float, float]:
        """Compute optimistic and pessimistic yield bands. @zara"""
        # Use measured variance where available
        measured = [m for m in months if m.source != "estimated"]
        if not measured:
            return expected * 1.15, expected * 0.85

        # Compute coefficient of variation from daily averages
        daily_values = [m.measured_avg_daily_kwh for m in measured if m.measured_avg_daily_kwh > 0]
        if len(daily_values) < 3:
            return expected * 1.15, expected * 0.85

        mean_val = sum(daily_values) / len(daily_values)
        variance = sum((v - mean_val) ** 2 for v in daily_values) / len(daily_values)
        cv = math.sqrt(variance) / mean_val if mean_val > 0 else 0.15

        # Bound the variation to a reasonable range
        spread = min(max(cv * 0.5, 0.08), 0.20)

        return expected * (1 + spread), expected * (1 - spread)

annual_forecast_reader.txt (21,8 KB)

4 „Gefällt mir“

Solar Forecast STATS V20 TFS (auch für ARM)

Die letzten Wochen habe ich viel Zeit und Arbeit in Stats gesteckt um es User-Freundlicher zu machen, übersichtlicher, strukturierter und selbsterklärender.

Home:
Die wichtigsten Solardaten übersichtlich und angepasst an die Eingaben von euch

Solar:
Weitere Informationen zur Solaranlage nun übersichtlich und leicht verständlich

Erergie:
Ich habe endlich die Zeit gefunden ein übersichtliches und reduziertes Energy-Dashboard zu bauen
Bei Fixen Tarifen kann alles entsprechend in der UI konfiguriert werden, das Highlight ist aber Dynamische Tarif, diese werden Stundengenau berechnet. Hierzu ist aber der GPM notwendig Stats arbeitet nathlos mit ihm zusammen,

Wärmepumpen, EV, Heizungen haben nun eine eigene Seite

Stats hat einen stark Vereinfachten Config-Flow und vieles lässt sich direkt aus STATS erledigen, wie z.B. das Laden des Akkus mit Netzstrom, Laden des Auto, Laden von Boilern..
Auch lassen sich Sensoren nun einfacher einbinden (Drop-Down)

Ich habe mich bewusst gegen die Anzeige / Berechnung einer marketing getriebenen klassischen „Amortisationszeit“ (ROI) entschieden, da sie betriebswirtschaftlich in die Irre führt - totaler Unsinn ist.

Warum?
Eine Solaranlage oder Wärmepumpe ist kein Konsumgut und keine reine Ausgabe – es ist ein wertsteigerndes Investment. Statt der Frage „Wann habe ich die Kosten raus?“ müsste man den Invest gegen die marktüblichen Zinsen stellen und die tatsächlichen Einsparungen. Das ist logischer und zeigt den tatsächlichen finanziellen Vorsprung gegenüber dem reinen Strombezug ohne Eigeninvestition.

Release gemeinsam mit TFS 2.0

7 „Gefällt mir“

UPDATE IS OUT!

ARM is back in the game!

Solar Forecast ML V20.0.2 — Stats Major Update

Fuel my late-night ideas with a coffee? I’d really appreciate it — keep this project running!

Buy Me a Coffee

Diese Version bringt die komplett überarbeitete Stats-Companion mit — das größte Update seit V17.

Neue Wetter-Seite Zwischen Solar- und Energie-Seite gibt es jetzt eine eigene Wetter-Seite mit fünf Cards: Hero mit aktuellem Zustand, 72h-Forecast, Strahlungswerte, Kleidungsempfehlung mit Astronomie und Wetter-History. Alles lokal berechnet aus den Blender-Daten — keine externen Wetterservices für die Anzeige.

Hubble AI — voller Stack-Readout Auf der Settings-Seite gibt es einen neuen Hubble-AI-Block, der den kompletten KI-Stack transparent macht: Aktives Modell, aktuelle Genauigkeit, Physics-Backbone-Status, Drift-Monitor. Keine Blackbox mehr — man sieht genau, was rechnet und wie gut es arbeitet.

Schatten-Fingerprint auf der Solar-Seite Neue Heatmap Monat×Stunde aus den gelernten Schattenmustern — zeigt auf einen Blick, wann über das Jahr welche Verschattung greift. Perfekt, um Baum- oder Gebäude-Schatten zu identifizieren, die man sonst nie gesehen hätte.

Consumer Detail Modals Wärmepumpe, Heizstab und Wallbox haben jetzt eigene Detail-Modals mit Live-Werten, konfigurierbar über den Consumers-Step im Config-Flow.

Pricing V2 Zwei-Step-Pricing (Modus wählen → Modus-spezifische Felder) und die Grundgebühr fließt jetzt korrekt in die monatlichen Kosten ein. Der Smart-Charging-Block bekam einen dedizierten Options-Step mit allen sechs Feldern.

Unter der Haube Rund 1,5 MB Dead-Code entfernt — alte Chart-Views, ungenutzte Reader, Legacy-Frontend-Dist, elf obsolete View-Klassen. Die Build-Pipeline wurde auf die neue 19-Dateien-Struktur umgestellt, die Originale sind jetzt read-only. Ein paar hartnäckige Bugs wurden gefixt: Full-KI-Toggle-Label, Billing-Month-Dropdowns, Februar-2026-Anomalie, Settings-leer-Bug.

Hinweis zum Transformer Die Integration mit dem Toorox ForeSight HA Add-on bleibt in dieser Version wie in V20.0.0 — adaptive Blend mit 35% Basis und drei Modulatoren (MAE, Cloud, Shadow). Der Blend greift automatisch, sobald das Add-on installiert ist.

Voraussetzungen

  • SFML ≥ 20.0.2
  • Solar Forecast Stats ≥ 20.0.0
  • Optional: Toorox ForeSight HA Add-on für den Transformer-Blend

Keine Datenbank-Migration nötig, bestehende Konfigurationen werden automatisch übernommen.

WICHTIGER HINWEIS IN DIESEM THREAD SCHREIBE NUR ICH ODER ADMIN`S BITTE NUTZT FÜR WEITERE FRAGEN DEN ENTSPRECHENDEN THREAD

4 „Gefällt mir“

UPDATE INFO TFS HA

in ca. 23 Std sollte das Training erledigt sein. Der Transformer läuft bereits und wurde komplett umgebaut.. hier weitere Informationen:

Toorox ForeSight HA — V2 “Phoenix”

TFS ist ein optionaler Side-Kick zu SFML. Der Kern-Forecast kommt von SFML (Physics + Ridge + LSTM + MLP + Kalman). TFS steuert einen eigenen Transformer-basierten Forecast bei, der in Schlechtwetterlagen und Verschattungsszenarien vom SFML-Ensemble stärker gewichtet wird.

Bei klarem Himmel dominieren Physik und klassische Modelle — dort bekommt TFS weniger Gewicht. Bei Overcast/Fog/Verschattung steigt das Gewicht auf bis zu 55 % des finalen Forecasts.

Architektur

  • iTransformer + PatchTST Hybrid mit Conditional Layer Norm (FiLM) für Panel-Geometrie-Konditionierung
  • ~22 M Parameter (Chinchilla + Pretrain-Korpus)
  • 168 h Kontext → 72 h Forecast mit Multi-Variate Weather Cross-Attention
  • Gain-Learning Physik-Baseline (identische Formel zu SFMLs PhysicsEngine)
  • P10 / P50 / P90 Quantile mit monotoner Parametrisierung
  • LoRA Rank-8 per-Instanz Finetune (Base-Modell bleibt frozen)
  • Progressive Horizon Curriculum (24 h → 48 h → 72 h) im Pretrain

API

GET  /health                                   → {"status": "ok", ...}
POST /api/forecast/run?forecast_type=sfml      → SFML-contract (p50 only)
GET  /api/forecast/quantiles?date=YYYY-MM-DD   → P10/P50/P90 (für SFML-Stats)
POST /api/lora/refinetune[?force=true]         → Admin: Finetune-Trigger
GET  /api/admin/status                         → State + Adapter-Info

Datenfluss

SFML-DB (read-only)    (ICON, GFS, ECMWF)   BrightSky
     │                         │                          │
     ▼                         ▼                          ▼
[History: 168h actuals+weather]      [Weather Blender + CloudType]
     │                                           │
     └───────────────┬───────────────────────────┘
                     ▼
           [Feature Extraktion (28+3)]
                     ▼
         [Phoenix Transformer (Base + LoRA)]
                     ▼
         [Gain × Baseline]
                     ▼
       [Physics Post-Processing]
                     ▼
   [TFS State-DB: p10/p50/p90 + Cache]
                     │
                     ▼
      SFML ruft /api/forecast/run → p50 blend
      SFML-Stats ruft /api/forecast/quantiles → UI

Architektur

Docker-Image basiert auf python:3.14.2-slim-bookworm (nicht Alpine, wegen PyTorch Wheels).

Pretrain-Weights: models/base/TFS-V2_pretrain_*.safetensors LoRA-Adapter: /config/toorox_foresight_ha/lora/lora_<instance>.safetensors State-DB: /config/toorox_foresight_ha/tfs.db

Training

Finetune:

# Auto (skip wenn Adapter aktuell)
--force   # Erzwinge Neu-Kalibrierung

Konstanten

Konstante Wert Quelle
EMA_ALPHA im Physics-Calibrator 0.22 SFML V16.4.0 Lesson
Gain-Range [0.0, 1.3] Physics-plausibel inkl. Cloud-Enhancement
Baseline γ_pmp -0.004 /K Crystalline Si Standard
NOCT 45 °C Standard-Panel
System-Efficiency 0.90 SFML-kompatibel
Albedo 0.2 SFML-kompatibel
LoRA-Rank 8 Chinchilla-sicher bei 800+ Samples

Die ersten Forecasts ohne Fine-Tuning sehen sehr vielversprechend aus..!

Stunde                  Gesamt   Gr.1   Gr.2  Cloud
----------------------------------------------------------
2026-04-21T07:00:00+02:00  0.058  0.039  0.019  clear
2026-04-21T08:00:00+02:00  0.211  0.137  0.074  clear
2026-04-21T09:00:00+02:00  0.441  0.275  0.166  clear
2026-04-21T10:00:00+02:00  0.728  0.442  0.286  clear
2026-04-21T11:00:00+02:00  1.007  0.603  0.405  clear
2026-04-21T12:00:00+02:00  1.216  0.723  0.493  clear
2026-04-21T13:00:00+02:00  1.310  0.777  0.532  clear
2026-04-21T14:00:00+02:00  1.257  0.750  0.506  clear
2026-04-21T15:00:00+02:00  1.093  0.658  0.435  cirrus
2026-04-21T16:00:00+02:00  0.824  0.505  0.318  cirrus
2026-04-21T17:00:00+02:00  0.614  0.383  0.232  cirrus
2026-04-21T18:00:00+02:00  0.366  0.235  0.131  cirrus
2026-04-21T19:00:00+02:00  0.176  0.118  0.058  clear
2026-04-21T20:00:00+02:00  0.004  0.003  0.002  clear
----------------------------------------------------------
TOTAL                    9.31   5.65   3.66  kWh

Ich konnte Last auf die HW während des Tränings um 90% reduzieren, was eine deutlich breiterer HW auf dem Host zulässt.. das ist unglaublich!

2026-04-20T16:42:45.511576Z [info     ] finetune_dataset_complete      total_samples=50 valid_ratio=50/50
2026-04-20T16:42:45.513244Z [info     ] finetune_dataset_ready         samples=50
2026-04-20T16:42:46.275784Z [info     ] finetune_training_start        device=cpu lora_rank=8 max_epochs=5 samples=50 train_size=40 val_size=10
2026-04-20T16:43:00.761739Z [info     ] finetune_epoch                 best_val=n/a elapsed=14.5s epoch=1/5 train_loss=0.10059 val_loss=0.09568
2026-04-20T16:43:14.242626Z [info     ] finetune_epoch                 best_val=0.09568 elapsed=28.0s epoch=2/5 train_loss=0.10074 val_loss=0.09568
2026-04-20T16:43:29.193445Z [info     ] finetune_epoch                 best_val=0.09568 elapsed=42.9s epoch=3/5 train_loss=0.10066 val_loss=0.09568
2026-04-20T16:43:29.193620Z [info     ] finetune_early_stop            epoch=3
2026-04-20T16:43:29.202604Z [info     ] finetune_complete              adapter=/config/toorox_foresight_ha/lora/lora_default.safetensors elapsed=42.9s epochs=3 val_loss=0.09568
2026-04-20T16:43:29.207361Z [info     ] finetune_done                  epochs=3 samples=50 status=success 

Das “Validation-Skript” funktioinert und baut die Samples nun selber und filtert falsche Werte heraus. Genau das was erreicht werden soll(te)

2026-04-20T17:41:35.026981Z [info     ] finetune_dataset_progress      offset=10/200 samples=10
2026-04-20T17:42:16.104827Z [info     ] finetune_dataset_progress      offset=20/200 samples=20
2026-04-20T17:42:57.175921Z [info     ] finetune_dataset_progress      offset=30/200 samples=30
2026-04-20T17:43:35.924871Z [info     ] finetune_dataset_progress      offset=40/200 samples=40
2026-04-20T17:44:17.918183Z [info     ] finetune_dataset_progress      offset=50/200 samples=50
2026-04-20T17:44:57.710230Z [info     ] finetune_dataset_progress      offset=60/200 samples=60
2026-04-20T17:45:36.313844Z [info     ] finetune_dataset_progress      offset=70/200 samples=70
2026-04-20T17:46:18.301246Z [info     ] finetune_dataset_progress      offset=80/200 samples=80
2026-04-20T17:46:58.301932Z [info     ] finetune_dataset_progress      offset=90/200 samples=90

Durch de neue Möglichkeit selbst zu entscheiden, wann das FT durchgeführt werden soll, ist es auch einfacher es in auf seine eigenen Backup-Routinen einzustellen.

Die Quantillen spreizen noch nicht genug.. eigentlich überhaupt nicht Das muss ich noch überprüfen - > Bug

2026-04-20T17:17:31.222205Z [info     ] forecast_complete              groups=2 horizon=72h lora=active p10=26.8kWh p50=26.8kWh p90=26.8kWh

Status: Code läuft → Model ist noch nicht fertig trainiert
RELEASE: Geplant Donnerstag

2 „Gefällt mir“

Update: Bugfixes SFML STATS

Bei komplexen Systemumstellungen lassen sich vereinzelte Fehler leider nicht immer vollständig vermeiden. Ich danke der Community herzlich für das konstruktive Feedback und die zahlreichen Fehlermeldungen!

Behobene Fehler (Fixes)

  • Mobil-Skalierung: Die allgemeine Darstellung wurde optimiert.

Hinweis: Das Abrechnungsboard auf der Energie-Seite kann technisch bedingt nicht weiter verkleinert werden, weshalb es hier auf Mobilgeräten weiterhin zu Einschränkungen in der Darstellung kommen kann.

  • GHI-Wert (Globalstrahlung) zeigt 0: Fehler behoben. Falls keine Wetterstation vorhanden ist oder Sensorwerte im falschen Format vorliegen, greift das System nun auf einen Datenbank-Fallback zurück.

  • Panel-Gruppen Settings: Fehlende Werte werden nun korrekt aus der Datenbank geladen.

  • Graph-Skalierung: Die Skalierung wurde angepasst und startet/endet nun korrekt auf der Nulllinie (Baseline).

  • Astronomie-Daten (Wetter-Seite): Aufgrund eines Fehlers in der Standard „Sun“-Entität von Home Assistant (HA) wurde die Datenquelle auf SFML-Astrodaten umgestellt.

  • CSV-Export: Die Export-Funktion ist wieder vollständig einsatzbereit.

  • Live-Power Zeitachse: Die Anzeige wurde korrigiert und zeigt nun ausschließlich Daten des aktuellen Tages anstatt des Vortages.

  • Inkonsistente Zeitachsen: Die Achsenbeschriftungen der Graphen wurden vereinheitlicht.

  • Prognose-Ansicht: Die erwartete Produktion für die Folgetage wurde hinzugefügt.

  • Prognose-Rauschen: Unerwünschte Datenpunkte („Noise“) vor Sonnenaufgang und nach Sonnenuntergang wurden entfernt.

RELEASE: Mit dem Update TFS 2.0 (geplant für Donnerstag)

10 „Gefällt mir“

UPDATE V20.2.0 → out

Dieses Update enthaelt funktionale Verbesserungen in SFML selbst sowie sichtbare Fixes und Erweiterungen in den zugehörigen Stats-Ansichten. Es ist auch die Grundlage für das Transformer-Update

Wichtige Verbesserungen in SFML

  • Die Behandlung von Panel-Gruppen wurde deutlich robuster gemacht.
  • Bestehende Gruppen-Konfigurationen koennen jetzt sauberer migriert werden, auch wenn sich Namen, Aufteilung oder Zuordnung im Laufe der Zeit ändern. @dietmar1968
  • Die interne Gruppenlogik wurde so erweitert, dass bestehende Lern-, Verlauf- und Zuordnungsdaten stabiler uebernommen werden können. @dietmar1968
  • Die Konfiguration der Panel-Gruppen wurde überarbeitet und klarer beschrieben.
  • Der zusaetzliche Tagesenergie-Sensor ist jetzt im Eingabeformat flexibler behandelbar und fuer das gruppenspezifische Lernen besser erklaert.
  • Home-Assistant- und HACS-Metadaten wurden auf einen konsistenten Stand gebracht.

SFML Stats

  • Die Solar-Seite wurde bei der Gruppenanzeige korrigiert.
  • Die bisherige Begrenzung auf zwei feste Panel-Gruppen wurde entfernt.
  • Dynamische Gruppen aus den API-Daten werden jetzt auch im Frontend korrekt dargestellt.
  • Die Statusdarstellung fuer Panels wurde verbessert:
    • Prognose-only Panels werden nicht mehr als frei fehlinterpretiert
    • fuer diese Panels wird jetzt ein passender Prognose-Status angezeigt
    • fehlende reale Ist-Daten fuehren nicht mehr zu irrefuehrenden Effizienzangaben
  • Eine neue Live-Energiefluss-Ansicht wurde vorbereitet bzw. integriert, um Solar, Haus, Akku, Netz und optionale Verbraucher uebersichtlicher darzustellen.
  • Mehrere Dashboard-Seiten wurden in Darstellung und Responsiveness ueberarbeitet, damit die Oberflaeche auf unterschiedlichen Ansichten sauberer reagiert.
  • Ansicht für Mobilgeräte wurde verbessert
  • Neue Anzeige für Schatten auf den Panelen

Fuel my late-night ideas with a coffee? I’d really appreciate it — keep this project running!

Buy Me a Coffee

Verfügbar via HACS

8 „Gefällt mir“

UPDATE TFS HA → out

HINWEIS:

  • Sollte es während des Updates zu einer Fehlermeldung kommen, bitte zuerst die “alte” Version deinstallieren.
  • Es gibt keine mindest HW-Anforderung → der gesunde Menschenverstand ist hier gefragt
  • Bitte unbedingt das Fine-Tuning durchlaufen lassen! → Je nach HW 20 Min - 1 Std

Toorox ForeSight HA 22.0.0 für alle Systeme (x86 / ARM)

Möchtest du meine nächtlichen Ideen mit einem Kaffee unterstützen? Ich würde mich sehr freuen — damit dieses Projekt weiterlaufen kann!**

Buy Me a Coffee

Dieses Release markiert den Übergang zur neuen Phoenix V2 Generation von Toorox ForeSight HA.

Phoenix V2 führt eine deutlich modernisierte Vorhersagearchitektur ein und schafft die Grundlage für die nächste operative Ausbaustufe des Toorox Solar-Intelligence-Stacks. Das Update bringt einen neuen transformer-basierten Forecast-Kern, eine neu gestaltete Runtime-Pipeline, eine verbesserte Systemstabilität sowie eine geschützte Modellbereitstellung für den Produktionseinsatz.

Was ist neu

  • Einführung der neuen Phoenix V2 Forecast-Runtime
  • Neuer transformer-basierter Forecast-Kern für den Ensemble-Betrieb
  • Überarbeitete Forecast-Pipeline, API-Struktur und Runtime-Abläufe
  • Verschlüsselte Auslieferung des Base-Modells für geschützte Release-Builds
  • Verbesserungen bei Scheduler-Verhalten, Startlogik und Runtime-Stabilität
  • Automatische Erkennung veralteter Adapter mit geführtem Re-Fine-Tuning
  • Aktualisierte Home-Assistant-App-Metadaten, Branding und Dokumentation

Wichtig für bestehende Installationen

  • Dies ist ein großes Plattform-Update und kein gewöhnliches Wartungsrelease.
  • Die interne Runtime-Struktur und mehrere Kernkomponenten wurden grundlegend überarbeitet.
  • Falls ein vorhandener Fine-Tune-Adapter nicht mehr zum aktualisierten Base-Modell passt, erkennt das System dies automatisch und startet einen neuen Fine-Tuning-Zyklus.

Release-Hinweis

Diese Version wurde aus dem aktuell validierten, produktionsreifen Runtime-Stand erstellt und enthält das verifizierte verschlüsselte Phoenix V2 Base-Modell für die neue Release-Linie.

2 „Gefällt mir“

Heute ist großer Update-Tag: Einen habe ich noch:

UPDATE WEATHER FUSION AI

→ online bei HACS

Zunächst einmal vielen Dank für die Geduld und eure Meldungen! Ich habe alles im Rahmen der Möglichkeiten umgestzt und gefixt.. !

Viel Spaß!

Spendierst du mir einen Kaffee für meine nächtlichen Ideen? Ich würde mich sehr freuen – so hilfst du dabei, dieses Projekt am Laufen zu halten!

Buy Me a Coffee

Dieses Release konzentriert sich auf Stabilität, Vorhersagequalität und einige wichtige Fehlerbehebungen, die von der Comunity gemeldet wurden.

Behoben

  • Ein Tag/Nacht-Problem wurde behoben, durch das tagsüber fälschlich Mond- bzw. clear-night-Symbole angezeigt werden konnten
  • Die Behandlung von Wetterzuständen in Tageszusammenfassungen wurde verbessert
  • Unterstützung für die vorhergesagte Windrichtung wurde zur täglichen Vorhersage hinzugefügt
  • Lokale Windrichtungssensoren wurden in den Konfigurationsfluss und das Tracking eingebunden
  • Die Versionsanzeige wurde korrigiert, sodass die Integration jetzt die richtige Release-Version ausgibt
  • Die Behandlung der Vorhersagegenauigkeit wurde verbessert, sodass bei unvollständiger Kalibrierung keine irreführenden Werte mehr angezeigt werden

WICHTIGER HINWEIS IN DIESEM THREAD SCHREIBE NUR ICH ODER ADMIN`S BITTE NUTZT FÜR WEITERE FRAGEN DEN ENTSPRECHENDEN THREAD

4 „Gefällt mir“

Update SFML und STATS - Prognosegenauigkeit

Einige User haben berichtet das die Prognosegenuigkeit teilweise 30% abweicht. Nach Prüfung von Logs und DB war klar das es am MPPT-Throtteling liegt. IST und Prognose driften auseinander und erzeugen Abweichungen die Formal richtig oder sachlicht falsch sind.
Um das Problem zu adressieren wird im kommenden Update Clean-Evaluation in Stats eingebunden und Attribute in der Sensorplattform. Das gibt auch die Möglichkeit seine eigene Anlage besser zu planen - Lohnt sich ein größerer Akku, mehr Panels..

Solar Forecast

  • Vorbereitung für die MacOsX Schnittstelle im Code hinterlegt

Solar Forecast STATS

  • Vorbereitung für die MacOsX Schnittstelle zum Hinzufügen von Shelly, Fronius, Anker..
  • Wetterdaten sind zurück auf der Startseite

Releas: Donnerstag nach EOD

12 „Gefällt mir“

So, das Update steht kurz vor dem Release. Deshalb hier noch einmal der finale Überblick, was sich wirklich geändert hat und was euch das im Alltag bringt.
Den alten Beitrag kann ich leider nicht mehr bearbeiten, daher dieser neue Post.

SFML

Mit TFS ist jetzt nicht einfach nur „noch ein Forecast“ dazugekommen, sondern ein zusätzlicher Layer, der SFML sinnvoll erweitert. Genau daraus entsteht einer der wichtigsten neuen Vorteile dieses Updates:

Es kommt eine neue Planungsprognose (P10-Blend).

Dieser Sensor ist bewusst konservativer ausgelegt und richtet sich an alle, die nicht einfach nur eine Prognose sehen wollen, sondern eine belastbare Grundlage für echte Entscheidungen brauchen:

  • Wallbox laden
  • Wärmepumpe steuern
  • Akku laden
  • Verbraucher gezielt einplanen

Kurz gesagt:
Mehr nutzbare Planungsgrundlage. Der neue Sensor ist eine Ergänzung für alle, die lieber sicher planen als zu optimistisch rechnen.

Zusätzlich wurde die Prognose insgesamt robuster:

  • bessere Reaktion auf wechselnde Wetterlagen
  • stabilere Datenbasis
  • sauberere Lerngrundlage
  • mehrere bekannte Fehler und Unsauberkeiten wurden behoben

Gerade im Alltag heißt das am Ende vor allem eins: mehr Verlässlichkeit.

STATS

In STATS steckt in diesem Update die meiste Arbeit. Und hier war das Ziel nicht Optik, sondern Substanz und es halte Dinge Einzug, die ich seit Wochen teste.

Der größte Fortschritt ist die Prognosegüte.
Bisher war diese in bestimmten Szenarien oft nur begrenzt brauchbar oder wurde komplett missverstanden, zum Beispiel bei:

  • Balkonkraftwerken
  • Null-Einspeisung
  • vollem Speicher
  • MPPT-Drosselung
  • Inverter-Clipping
  • größeren Anlagen mit technischer Begrenzung

Das Problem:
Die Statistik sah oft schlechter aus, als die eigentliche Prognose wirklich war, weil technische Begrenzungen und reale Erzeugung oft auseinanderlaufen können.

Genau das wurde jetzt deutlich sauberer gelöst.

STATS bewertet die Prognose jetzt wesentlich realistischer:

  • bis zu dem Punkt, an dem technische Begrenzungen eingreifen
  • und zusätzlich weiterhin im realen Gesamtverlauf

Das macht die Auswertung fairer, verständlicher und im Alltag endlich viel nützlicher.

Dazu kommen weitere Verbesserungen:

  • sauberere Berechnung der Prognosequalität
  • bessere Behandlung von MPPT-Drosselung und Clipping
  • korrekt arbeitende Ausschluss- und Durchschnittswerte
  • spürbar bessere Stunden- und Tagesauswertungen
  • mehrere Fehlerbehebungen in der Statistiklogik

Unterm Strich

Dieses Update bringt vor allem drei Dinge:

  • mehr Planbarkeit
  • mehr Verlässlichkeit
  • mehr Aussagekraft

Weniger Kosmetik, mehr echter Nutzen.

Vor allem Nutzer mit Speicher, Wallbox, Wärmepumpe, Balkonkraftwerk, Null-Einspeisung oder größeren Anlagen werden den Unterschied sehr deutlich merken und sich auf SFML bei ihrer Planung verlassen.

Beispiel:

Release MIttwoch Abend

12 „Gefällt mir“

UPDATE IS OUT… (online via HACS)

Da das Update einige größeren Änderungen unter der Haube beinhaltet (wie oben beschrieben) bitte bei einem Fehler die vorherige Version erneut laden und mir den Fehler mitteilen— (nachdem ihr die 2 Neustarts (1. SFML und dann STATS) gemacht habt, der ist diesen mal besonders wichtig!

Ihr wisst ja HACS und so… :frowning:

6 „Gefällt mir“

TFS HA APP Update auf Version 26.0.0

Fuel my late-night ideas with a coffee? I’d really appreciate it — keep this project running!

Buy Me a Coffee

Änderungen:

  • Lerndatei für Transfomer aktualisiert
  • Vorbereitung für Hybrid-Prognose implementiert
  • Wetter-KI Log hinzugefügt
  • Transformer-KI Lernen-LOG hinzugefügt
  • Datenbank Optimierungen
  • Performance Tweaks
  • SFML EOD Ergebniss wird nun automatisch von TFS auf relevante Änderungen geprüft.
  • Guardrails angepasst
  • Neue Guardrails bei der Lerngüte, neues Modell wird nur übernommen, wenn es besser oder gleich dem vorherigen ist

WICHTIGER HINWEIS:

  1. Bitte das Lernen im Konfigurationsmenü ausserhalb der Produktionszeit / Backupzeit legen z.b. 02:30 Uhr. Standart ist 23:30 Uhr, dass kann auf schwächeren Systemen zu Hic-Ups und kurzeitigen Event-Loop Blockierugen führen, da zur selben Zeit SFML den EOD durchführt!
  2. Nach dem Update dauert der Lernlauf länger, da ich die SQL Contrains angepast habe für das kommende Update und Änderungen von SFML und STATS.. dadurch wird die Schnittstelle neu gebaut. bitte Geduld mitbringen!

Relase: Erfolgt, kommt automatisch

Nochmals zur Erinnerung an Alle:

Hier postet nur @Tom-HA seine Release-Informationen!!

Diskussionen um den jeweiligen Release bitte nur hier:

oder
https://community.simon42.com/t/sfml-feedback-und-problemberichte-zum-aktuellen-release/80551?u=johnny_1993
oder
https://community.simon42.com/t/tfs-ha-solar-forecat-ml-8-head-transformer-ki-app-version/83538?u=johnny_1993

Die übrigen Diskussionsbeiträge von hier wurden/werden in den Diskussionsthread verschoben.

1 „Gefällt mir“

UPDATE SFML + STATS auf Version 26 “Hubble Hybrid”

Eine wichtige Info vorweg:

Mit Version 26 habe ich im Hintergrund sehr viel umgebaut. Der komplette AI-Stack von SFML wurde dafür zerlegt und neu neu aufgebaut, damit die neue Hybrid-Prognose überhaupt sauber möglich wird. In diesem Zuge habe ich auch STATS technisch weitgehend neu aufgebaut und an die neue Struktur angepasst.

So ein großer Umbau ist immer ein Kraftakt. Ich habe alles so sauber wie möglich umgesetzt und ausführlich geprüft. Trotzdem will ich offen sagen: Das hier ist keine Beta, aber es ist definitiv eine Version mit sehr tiefen Eingriffen. Deshalb kann es an einzelnen Stellen trotz aller Vorsicht noch mal zu kleineren Problemen oder unerwartetem Verhalten kommen.

Was ist neu in SFML?

1. Neue Hybrid-Prognose

Das zentrale neue Feature in Version 26 ist die Hybrid-Prognose.

Dabei werden die bisherigen SFML-Mechanismen nicht einfach ersetzt, sondern um eine neue zusätzliche Ebene erweitert. Ziel ist, die Prognose robuster und flexibler zu machen, ohne die Stärken der bisherigen Logik zu verlieren.

Ein wichtiger Punkt dabei:
Die klassische SFML-Prognose bleibt als Kern erhalten. Die neue Hybrid-Prognose kommt zusätzlich dazu und erweitert das System.

2. Kompletter Umbau des AI-Stacks

Damit die Hybrid-Prognose sauber funktioniert, habe ich den internen AI-Bereich grundlegend neu strukturiert.

Das betrifft unter anderem:

  • die interne Aufteilung der Prognose-Logik
  • die Zusammenarbeit von AI, Physik und Wetterbewertung
  • die Verarbeitung von Teilprognosen und Kombinationslogik
  • die technische Grundlage für spätere Erweiterungen

Kurz gesagt:
Version 26 ist nicht einfach ein kleines Feature-Update, sondern ein echter Umbau an der Basis.

3. Bessere Trennung von Kernsystem und Zusatzlogik

Ich habe mehrere Bereiche sauberer voneinander getrennt.

Das ist wichtig, damit:

  • neue Funktionen besser ergänzt werden können
  • bestehende Prognosepfade stabil bleiben
  • spätere Weiterentwicklungen weniger Nebenwirkungen erzeugen
  • Fehler besser nachvollzogen und behoben werden können

4. Verbesserungen bei Wetter- und Prognose-Logik

Rund um Wetterbewertung, Prognoseverhalten und Tagesverlauf gab es ebenfalls mehrere Anpassungen.

Dazu gehören unter anderem:

  • robustere Verarbeitung bei schwierigen Wetterlagen
  • bessere Behandlung von starker Bewölkung
  • sauberere interne Abläufe bei Morgenprognose, Tagesverlauf und Nachbearbeitung
  • Korrekturen an Stellen, an denen Wetter- und Prognosewerte sich gegenseitig ungünstig beeinflussen konnten

5. Mehr Stabilität im Hintergrund

Ich habe zusätzlich an mehreren technischen Grundlagen gearbeitet, damit SFML im Alltag stabiler läuft.

Das betrifft zum Beispiel:

  • Datenbankzugriffe
  • interne Schreibvorgänge
  • Startverhalten
  • Wiederherstellung nach Neustarts
  • Status- und Verlaufsdaten

Das sieht man nicht direkt in der Oberfläche, ist aber wichtig für einen ruhigen Betrieb.

Was hat sich bei STATS geändert?

Auch STATS wurde für Version 26 deutlich umgebaut.

Der Grund ist einfach:
Wenn sich die Grundlage in SFML so stark verändert, muss STATS passend mitgezogen werden. Sonst würden Anzeigen, Auswertungen und Zusammenhänge irgendwann nicht mehr sauber stimmen.

Dabei habe ich STATS so angepasst, dass:

  • die neuen SFML-Strukturen sauber unterstützt werden
  • Prognose- und Auswertungsdaten besser zusammenpassen
  • künftige Erweiterungen besser möglich sind
  • die technische Basis insgesamt moderner und belastbarer wird

Wichtig nach dem Update

Nach so einem großen Versionssprung kann es sein, dass einzelne Dinge sich erst einmal neu einpendeln müssen.

Deshalb mein klarer Hinweis:

  • bitte nach dem Update genau beobachten
  • wenn etwas komisch aussieht, bitte melden
  • besonders bei neuen Sensoren, Prognosewerten oder Anzeigen ruhig einmal genauer hinschauen

Ich habe sehr viel Arbeit in diese Version gesteckt, aber bei einem Umbau in dieser Größenordnung ist echtes Feedback aus dem Alltag besonders wertvoll.

Zusammengefasst:

Version 26 “Hubble Hybrid” ist eines der größten Updates seit langer Zeit.

Es bringt nicht nur neue Funktionen, sondern vor allem eine neue technische Grundlage für die nächsten Schritte. Genau deshalb war dieser Umbau nötig. Er schafft die Basis für die Hybrid-Prognose und für alles, was darauf später noch aufbauen kann.
Wenn irgendwo noch etwas zickt, schaue ich mir das natürlich an und arbeite es nach. Genau dafür ist so ein großes Release am Anfang immer auch ein Stück Feinarbeit im echten Betrieb, da ich nicht alle Systeme virtuell prüfen kann!

Hybrid Prognose aktivieren:

Bitte beachten: Zusätzliche Prognosen = zusätzliche Rechenleistung!

Neue Funktionen in STATS

Neue und bessere Logik bei der Prognosegüte Berechnung, Wetter-KI wird nun angezeigt ( Das ist keine Wetter-Prognose, sondern eine Solar-Prognose!)
Neue Graphen und die letzte Einstellung wird nun korrekt gespeichert

2D ist zurück (Beta-Test)

Neue und bessere Ansicht der Lernstunden und echten Prognose-Güte

Fuel my late-night ideas with a coffee? I’d really appreciate it — keep this project running!

Buy Me a Coffee

RELEASE: 08. Mai

12 „Gefällt mir“