Installation von Toorox ForeSight HA als Docker-Container für Home Assistant in der Docker Version

Dank Unterstützung von @Tom-HA ist es mir gelungen, Toorox Foresight HA als eigenständigen Docker Container, und nicht als HA-Addon (bzw. App) auf einem Intel-NUC unter Ubuntu zu nutzen. Ich verwende die HA-Version für Docker, weil bei mir die “supervised version” unter HAOS in einer virtuellen Maschine auf dem NuC instabil war. Es kam immer wieder zur Korruption der HA home-assistant_v2.db kam. Das ist nun Geschichte.

Wer sich für docker interessiert, kann zum Beispiel hier https://www.youtube.com/playlist?list=PL3o767Uczze7VyWBHumFZPSzMRNbtgl-l die Grundlagen erlernen. Wie man HA als docker container intallieren kann, ist z. B. hier gut erklärt: https://www.youtube.com/watch?v=S-itdbqwj4I&t=6s

Umgebung:
Ich verwende auf dem NUC Ubuntu 24.04.4 LTS. Unter “/opt” habe ich eine einzige “docker-compose.yaml” für die Konfiguration aller Container. Dort sind auch die Unterverzeichnisse für alle “exposed directories” der Container angelegt.

Docker Image erzeugen:
Zunächst ist die aktuelle SW-Version https://github.com/Zara-Toorox/TFS-HA-SFML-Transformer-AI/archive/refs/tags/V28.0.0.tar.gz herunter zu laden und in einem Verzeichnis nach Wunsch zu entpacken.

Dann in das Verzeichnis toorox_foresight_ha wechseln. Um das Image zu bauen, muss das dort gespeicherte “Dockerfile” angepasst werden, da es eigentlich für die HAOS-Umgebung geschrieben ist. Die erste Zeile enthält ARG BUILD_FROM. Ich habe sie auf ARG BUILD_FROM=ubuntugeändert.

Dann in diesem Verzeichnis
docker build -t toorox_foresight_ha .
ausführen. Der Build sollte beginnen. Auf dem NUC hat er ~200 sec gedauert. Das Image ist dann im Docker System bekannt und kann per docker compose geladen werden.

Hier mein Eintrag für toorox_foresight_ha in der docker-compose.yaml:

  toorox-foresight:
    image: toorox_foresight_ha:latest
    container_name: toorox-foresight
    restart: unless-stopped
    ports:
      - "8780:8780"
    volumes:
      - /opt/homeassistant/config:/config
    environment:
      - TFS_TIMEZONE_STR=Europe/Berlin
      - TFS_LATITUDE=52.000000
      - TFS_LONGITUDE=9.000000
      - TFS_STATE_DIR=/config/toorox_foresight_ha

Da diese TFS-Version als HA-Addon gedacht ist, muss sie auf das “config”-Verzeichnis von HA zugreifen können, um dort die SFML-Datenbank direkt verwenden zu können. Deshalb wird /config auf das config-Verzeichnis des HA-Containers gemapped. Nachfolgend werden die 4 TFS_xxx Umgebungsvariablen definiert, da diese Daten von TFS als Container nicht von HA abgerufen werden können. Für LAT und LON natürlich den eigenen Standort eintragen :).

Der HA-Container muss im “network: host” Mode laufen, wie hier von ZARA erläutert: SFML: Feedback und Problemberichte zum aktuellen Release - #828 von Tom-HA

Meine Config für HA im docker-compose.yaml sieht so aus:

  homeassistant:
    container_name: homeassistant
    image: "ghcr.io/home-assistant/home-assistant:stable"
    volumes:
      - /opt/homeassistant/config:/config
      - /etc/localtime:/etc/localtime:ro
    restart: unless-stopped
    privileged: true
    network_mode: host

Damit funktioniert funktioniert der Zugriff des SFML auf das TFS.

Nun kann der TFS-Container gestartet werden. Dazu im Verzeichnis, wo die docker-compose.yaml liegt, dies Kommando ausführen:

docker compose up -d toorox-foresight .

Das Log vom Start des Containers kann man sich so anschauen:

docker logs toorox-foresight

Oder aber über portainer im GUI …

Hier das Log vom erstmaligen Start des TFS im Docker Container:

INFO:     Started server process [7]
INFO:     Waiting for application startup.
2026-06-17T07:44:11.944891Z [info     ] tfs_bootstrap                  codename=Phoenix state_dir=/config/toorox_foresight_ha version=28.0.0
2026-06-17T07:44:11.962563Z [info     ] state_db_apply_migration       description='Initial schema — forecasts, weights, baseline cache, training history' version=1
2026-06-17T07:44:11.964997Z [info     ] state_db_schema_ready          version=1
2026-06-17T07:44:11.965230Z [info     ] state_db_connected             path=/config/toorox_foresight_ha/tfs.db
2026-06-17T07:44:11.986666Z [info     ] runtime_context_resolved       latitude=52.2857943 location_source=settings longitude=9.28650475 panel_group_source=sfml panel_groups=3
2026-06-17T07:44:11.987237Z [info     ] weather_blender_initialized    rows=0
2026-06-17T07:44:12.642271Z [info     ] base_model_loaded              path=/app/models/base/TFS-V2_pretrain_best.safetensors.enc
2026-06-17T07:44:12.642587Z [info     ] lora_not_applied               instance=default status=missing_refinetune_pending
2026-06-17T07:44:12.646666Z [info     ] startup_notification           adapter_path=/config/toorox_foresight_ha/lora/lora_default.safetensors adapter_status=missing_refinetune_pending base_model_hash=9f032a4b93da1794 base_model_path=/app/models/base/TFS-V2_pretrain_best.safetensors.enc codename=Phoenix panel_groups=3 version=28.0.0
2026-06-17T07:44:12.649007Z [info     ] forecast_job_registered        kind=fixed spec=00:30
/usr/local/lib/python3.12/site-packages/tzlocal/unix.py:203: UserWarning: Can not find any timezone configuration, defaulting to UTC.
  warnings.warn("Can not find any timezone configuration, defaulting to UTC.")
2026-06-17T07:44:12.732257Z [info     ] forecast_job_registered        kind=solar_dynamic next_run_local=2026-06-18T04:15:33.419256+02:00 next_run_utc=2026-06-18T02:15:33.419256+00:00 spec=sunrise-45
2026-06-17T07:44:12.732622Z [info     ] finetune_job_registered        at=00:00 timezone=Europe/Berlin
2026-06-17T07:44:12.733531Z [info     ] scheduler_started              timezone=Europe/Berlin
2026-06-17T07:44:12.733777Z [info     ] startup_refinetune_queued      adapter_status=missing_refinetune_pending base_hash_adapter=None base_hash_current=9f032a4b93da1794 requested_at=2026-06-17T07:44:12.733749+00:00
2026-06-17T07:44:12.733906Z [info     ] startup_notification_refinetune reason=queued status=queued
INFO:     Application startup complete.
INFO:     Uvicorn running on http://0.0.0.0:8780 (Press CTRL+C to quit)
2026-06-17T07:44:12.763783Z [info     ] weather_learning_done          end_date=2026-06-16 samples=336 start_date=2026-06-02 status=success weights=32
2026-06-17T07:44:12.858340Z [info     ] finetune_dataset_building      actuals_count=1047 degradation_count=1440 end_date=2026-06-16 groups=3 max_samples=200 start_date=2026-04-17 step_hours=6 weather_count=1094
2026-06-17T07:44:44.633154Z [info     ] finetune_dataset_progress      offset=10/200 samples=10
2026-06-17T07:45:16.377015Z [info     ] finetune_dataset_progress      offset=20/200 samples=20
2026-06-17T07:45:48.065688Z [info     ] finetune_dataset_progress      offset=30/200 samples=30
2026-06-17T07:46:19.881185Z [info     ] finetune_dataset_progress      offset=40/200 samples=40
2026-06-17T07:46:51.580685Z [info     ] finetune_dataset_progress      offset=50/200 samples=50
2026-06-17T07:47:23.268277Z [info     ] finetune_dataset_progress      offset=60/200 samples=60
2026-06-17T07:47:55.042982Z [info     ] finetune_dataset_progress      offset=70/200 samples=70
2026-06-17T07:48:26.851659Z [info     ] finetune_dataset_progress      offset=80/200 samples=80
2026-06-17T07:48:58.564071Z [info     ] finetune_dataset_progress      offset=90/200 samples=90
2026-06-17T07:49:30.221736Z [info     ] finetune_dataset_progress      offset=100/200 samples=100
2026-06-17T07:50:01.887314Z [info     ] finetune_dataset_progress      offset=110/200 samples=110
2026-06-17T07:50:33.700380Z [info     ] finetune_dataset_progress      offset=120/200 samples=120
2026-06-17T07:51:05.370001Z [info     ] finetune_dataset_progress      offset=130/200 samples=130
2026-06-17T07:51:36.949277Z [info     ] finetune_dataset_progress      offset=140/200 samples=140
2026-06-17T07:52:08.693085Z [info     ] finetune_dataset_progress      offset=150/200 samples=150
2026-06-17T07:52:40.302139Z [info     ] finetune_dataset_progress      offset=160/200 samples=160
2026-06-17T07:53:11.985587Z [info     ] finetune_dataset_progress      offset=170/200 samples=170
2026-06-17T07:53:43.697992Z [info     ] finetune_dataset_progress      offset=180/200 samples=180
2026-06-17T07:54:15.330408Z [info     ] finetune_dataset_progress      offset=190/200 samples=190
2026-06-17T07:54:46.946381Z [info     ] finetune_dataset_progress      offset=200/200 samples=200
2026-06-17T07:54:46.946527Z [info     ] finetune_dataset_complete      total_samples=200 valid_ratio=200/200
2026-06-17T07:54:46.947126Z [info     ] finetune_dataset_ready         end_date=2026-06-16 samples=200
2026-06-17T07:54:47.552942Z [info     ] finetune_training_start        device=cpu lora_rank=8 max_epochs=20 samples=200 train_size=160 val_size=40
2026-06-17T07:55:38.294876Z [info     ] finetune_epoch                 best_val=n/a elapsed=50.7s epoch=1/20 train_loss=0.15800 val_loss=0.15458
2026-06-17T07:56:27.383703Z [info     ] finetune_epoch                 best_val=0.15458 elapsed=99.8s epoch=2/20 train_loss=0.15417 val_loss=0.14842
2026-06-17T07:57:16.471221Z [info     ] finetune_epoch                 best_val=0.14842 elapsed=148.9s epoch=3/20 train_loss=0.14777 val_loss=0.13952
2026-06-17T07:58:05.521378Z [info     ] finetune_epoch                 best_val=0.13952 elapsed=198.0s epoch=4/20 train_loss=0.13910 val_loss=0.12804
2026-06-17T07:58:55.279989Z [info     ] finetune_epoch                 best_val=0.12804 elapsed=247.7s epoch=5/20 train_loss=0.12810 val_loss=0.11587
2026-06-17T07:59:44.730056Z [info     ] finetune_epoch                 best_val=0.11587 elapsed=297.2s epoch=6/20 train_loss=0.11643 val_loss=0.10263
2026-06-17T08:00:34.336773Z [info     ] finetune_epoch                 best_val=0.10263 elapsed=346.8s epoch=7/20 train_loss=0.10227 val_loss=0.08260
2026-06-17T08:00:38.513437Z [info     ] weather_fetch_complete         hours=72 sources=['open_meteo', 'brightsky']
2026-06-17T08:00:42.429376Z [info     ] forecast_complete              groups=3 horizon=72h lora=missing_refinetune_pending p10=154.2kWh p50=164.2kWh p90=175.1kWh
2026-06-17T08:01:27.035547Z [info     ] finetune_epoch                 best_val=0.08260 elapsed=399.5s epoch=8/20 train_loss=0.06465 val_loss=0.05967
2026-06-17T08:02:16.730426Z [info     ] finetune_epoch                 best_val=0.05967 elapsed=449.2s epoch=9/20 train_loss=0.05129 val_loss=0.04781
2026-06-17T08:03:06.052188Z [info     ] finetune_epoch                 best_val=0.04781 elapsed=498.5s epoch=10/20 train_loss=0.04803 val_loss=0.04719
2026-06-17T08:03:55.440468Z [info     ] finetune_epoch                 best_val=0.04719 elapsed=547.9s epoch=11/20 train_loss=0.04633 val_loss=0.04512
2026-06-17T08:04:44.729002Z [info     ] finetune_epoch                 best_val=0.04512 elapsed=597.2s epoch=12/20 train_loss=0.04531 val_loss=0.04495
2026-06-17T08:05:34.217445Z [info     ] finetune_epoch                 best_val=0.04495 elapsed=646.7s epoch=13/20 train_loss=0.04451 val_loss=0.04373
2026-06-17T08:06:23.420460Z [info     ] finetune_epoch                 best_val=0.04373 elapsed=695.9s epoch=14/20 train_loss=0.04406 val_loss=0.04311
2026-06-17T08:07:12.676224Z [info     ] finetune_epoch                 best_val=0.04311 elapsed=745.1s epoch=15/20 train_loss=0.04362 val_loss=0.04327
2026-06-17T08:08:01.790034Z [info     ] finetune_epoch                 best_val=0.04311 elapsed=794.2s epoch=16/20 train_loss=0.04338 val_loss=0.04272
WARNING:  Invalid HTTP request received.
WARNING:  Invalid HTTP request received.
2026-06-17T08:08:51.006854Z [info     ] finetune_epoch                 best_val=0.04272 elapsed=843.5s epoch=17/20 train_loss=0.04320 val_loss=0.04289
2026-06-17T08:09:40.256852Z [info     ] finetune_epoch                 best_val=0.04272 elapsed=892.7s epoch=18/20 train_loss=0.04304 val_loss=0.04228
2026-06-17T08:10:29.473011Z [info     ] finetune_epoch                 best_val=0.04228 elapsed=941.9s epoch=19/20 train_loss=0.04297 val_loss=0.04245
2026-06-17T08:11:18.747963Z [info     ] finetune_epoch                 best_val=0.04228 elapsed=991.2s epoch=20/20 train_loss=0.04284 val_loss=0.04244
2026-06-17T08:11:18.778233Z [info     ] finetune_complete              adapter=/config/toorox_foresight_ha/lora/lora_default.safetensors elapsed=991.2s epochs=20 val_loss=0.04228
2026-06-17T08:11:18.799635Z [info     ] correction_learning_done       epochs=20 latest_learning_date=2026-06-16 samples=200 status=success val_loss=0.04228
2026-06-17T08:11:19.161034Z [info     ] base_model_loaded              path=/app/models/base/TFS-V2_pretrain_best.safetensors.enc
2026-06-17T08:11:21.219226Z [info     ] lora_applied                   instance=default val_loss=0.042282
2026-06-17T08:11:21.229127Z [info     ] lora_reloaded_after_finetune   status=active
2026-06-17T08:11:21.229713Z [info     ] learning_job_complete          correction_status=success status=completed weather_status=success

Die Uhrzeiten sind in UTC angegeben. Die gesamte Initialisierung hat bei also von 9:44 Uhr bis 10:11 Uhr gedauert, also ~27 Minuten.

Inzwischen sind 2 weitere Meldungen im Log aufgelaufen, die zeigen, dass TFS funktioniert:

2026-06-17T14:01:04.181225Z [info     ] weather_fetch_complete         hours=72 sources=['open_meteo', 'brightsky']
2026-06-17T14:01:06.645498Z [info     ] forecast_complete              groups=3 horizon=72h lora=active p10=154.4kWh p50=281.4kWh p90=287.6kWh

Viel Spaß beim Nachmachen und nochmal herzlichen Dank an @Tom-HA für seine Software!

2 „Gefällt mir“

@thomasz

vielen Dank für die klasse Anleitung und das Teilen deines Setups! Das ist ein extrem wertvoller Beitrag für alle, die Home Assistant in der Docker-Version betreiben und TFS-HA als eigenständigen Container laufen lassen wollen! Dein Setup ist die Königsklasse, da Du HA nicht in eine VM zwingst, sondern als Docker laufen lässt. Das ist eigentlich auch der 100% korrekte Weg (aus technischer Sicht) für Proxmox, VM, Linux, … da keine Resourchen verschwendet werden. HA gehört einfach nicht in eine VM auf einem Host. → meine persönliche Meinung.
Super Arbeit!!!

Der Kniff mit ARG BUILD_FROM=ubuntu im Dockerfile ist genau der richtige Weg, um das Ganze aus der HAOS-Add-on-Struktur zu lösen. Auch deine docker-compose.yaml mit dem Volume-Mapping auf das HA-Config-Verzeichnis und den manuellen Umgebungsvariablen ist super gelöst. Das spart anderen sicher eine Menge Ausprobieren.

Es freut mich sehr zu sehen, dass die Initialisierung und das Fine-Tuning bei dir so sauber durchgelaufen sind. Wenn Du magst, lass ich Dir gerne mal die Docker-Standalone (die große Vollversion) mit Sprachsteuerung und über 11 Mio. Parametern zukommen – dann kannst Du damit ein wenig spielen und testen!

Falls dir im weiteren Betrieb noch Optimierungspotenziale oder Besonderheiten auffallen, lass es mich wissen.

Danke für die Mühe, das so detailliert aufzuschreiben!

Zara

Danke für Dein Lob :slight_smile: . Die große “Vollversion” würde meinen NUC wohl überfordern. Zumindest jetzt noch hat er nur 8 GB Ram. Und TFS nimmt davon laut Portainer Stats schon 2,6 GB. HA 1,2, und dann gibt es da noch grafana, influxdb, msoquitto, openccu als container und als native Dienste evcc und den SteVe OCPP-Server. Den restlichen Pufferspeicher kann das System gut gebrauchen, denke ich …

tz@smart-nuc:/opt$ free -m
               gesamt       benutzt     frei      gemns.  Puffer/Cache verfügbar
Speicher:       7832        5525         378          14        2236        2306
Auslager:       4095         892        3203