Přeskočit obsah

Decision Log

Chronologický záznam klíčových rozhodnutí a závěrů pro lepší kontext v budoucích konverzacích.

Patří sem trvalá rozhodnutí, důležité trade-offy, omezení a lessons learned z incidentů nebo změn. Nepatří sem backlog, wishlist ani plánované změny, které ještě nejsou rozhodnuté nebo realizované.


2026-07-23 — Scrypted přesunut na pmx2; chronické replikační timeouty byly CPU hlad, ne ZFS

Kontext

GH #87 hlásil opakované selhání replikace VM 105 (zfs snapshot ... got timeout) a pbs-truenas: 500 read timeout. Issue směřovalo vyšetřování na ZFS, PBS a TrueNAS.

Zjištění

  • Všechny obviněné komponenty byly zdravé: rpool ONLINE (20 % plný, 0 chyb, žádné zamrzlé __replicate__ snapshoty), TrueNAS pooly ONLINE, PBS datastore dostupný. Klíčový ukazatel: IO pressure = 0, zatímco CPU pressure ~60 % a load ~11 na 12 jádrech.
  • Příčina: Scrypted (LXC 106) dělal AI detekci objektů na CPU. CT 106 spotřeboval ~50× víc CPU než zbytek uzlu. Timeouty padaly vždy v :30, kdy se replikační job sečetl s trvalou zátěží a zfs snapshot nestihl svůj tvrdý interní timeout. VM 105 byla oběť, ne zdroj. Chronické: 2–11× denně po 7 dní.
  • OpenVINO na AMD iGPU neakceleruje — slepá ulička. @scrypted/openvino je Intel framework (Intel CPU/iGPU/NPU). Oba uzly jsou AMD, takže inference běží čistě na CPU bez ohledu na to, že je iGPU passnutá. Passthrough /dev/dri/renderD128 má smysl jen pro hardwarové dekódování videa (VAAPI), ne pro detekci. Nezkoušet to znovu rozchodit — nejde to.

Rozhodnutí

  • CT 106 migrován pmx1 → pmx2. NFS bind mount /mnt/pve/nvr-truenas označen shared=1 (věcně správné — stejný export na obou uzlech, odblokuje migraci), offline ZFS send ~7,5 G / 1:16. Opraven chybný passthrough card0card1 (oba hosty mají card1/226:1).
  • Vědomá revize rozmístění z 2026-07-06, kde byl Scrypted záměrně na pmx1 a pmx2 vyhrazen pro pfSense + HA. Nové okolnosti: (a) chronické CPU zahlcení pmx1, (b) Scrypted drží zvonek, což ho řadí mezi kritické služby, ne mezi „ostatní". Původní rozhodnutí tuhle roli nezohledňovalo.
  • cpuunits: 50 na CT 106 jako pojistka co-rezidence s korunními klenoty — pod tlakem ustoupí pfSense/HA, ale když je volno, nic ho neomezuje (detekce zůstává plnorychlá). Hard cap by zbytečně zhoršil detekci.
  • Coral TPU zvažován a zamítnut. Spotřebu neospravedlní (3/4 roku běží dům z FTV, baterka kryje slabší dny), hluk není vnímán, další kamery se neplánují, a pmx2 má obrovskou rezervu. Přehodnotit jen při: přidání kamer navzdory plánu, nebo když se změřený inference time ukáže jako příliš vysoký (≳300 ms/snímek). Pokud se pro Coral někdy rozhodne, volit USB, ne M.2 — M.2 varianta tahá gasket DKMS modul, který se rozbíjí při upgradech Proxmox kernelu; USB jede přes libedgetpu v userspace bez kernel modulu.

Výsledek

pmx1 load 11,4 → 0,15, CPU pressure 60 % → 0,00 %. Replikace 105-0 prošla čistě (FailCount 0, 1,85 s). Žádné timeouty od migrace. GH #87 uzavřeno.

Reziduální riziko

Při údržbě pmx2 se Scrypted logicky vrátí na pmx1 a problém se ten den vrátí s ním. Řešit vypnutím Scryptedu po dobu údržby, ne hardwarem.

Vedlejší nález: NVR si retenci řídí sám

archive/nvr prořezán 3,09 T → 2,83 T bez zásahu → alert „Refquota exceeded 89,8 %" byl důsledek záměrně těsné kvóty, ne hrozby zaplnění. Dotažena fáze 2: refquota 3,15 T → 2,90 T. Do budoucna nečíst vysoké procento na archive/nvr jako incident.


2026-07-23 — pmx1 běží horko; teploty se nikde neměří (podezření u smrti starého pmx2)

Zjištění

Při diagnostice #87 se ukázalo, že teploty uzlů se nesbírají ani nealertujílm-sensors není nainstalovaný, ačkoli k10temp data poskytuje. Ruční odečet odhalil rozdíl, který nešel ignorovat:

pmx2 (ASUS PN54-S1) pmx1 (no-name)
load 0,95 0,69
Tctl 59 °C 81 °C
DMI ASUSTeK / ELM_PN54_S1 nevyplněné placeholdery

pmx1 je při nižší zátěži o 22 °C teplejší než pmx2. Vzorkování po 3 s ukázalo setrvalý růst 62 → 80 °C při pouhém load 0,42 → 0,70 — teplotní křivka extrémně citlivá na minimální zátěž. Před odlehčením nesl pmx1 Scrypted (4 jádra inference 24/7) týdny → reálně seděl u TjMax nonstop.

Hypotéza (nedokázaná — a to je pointa)

Starý pmx2 byl no-name mini PC se stejným CPU Ryzen 5 7430U, umřel 2026-07-05 náhlou smrtí desky bez shutdown sekvence (zdroj vyloučen výměnou adaptéru) — a hostoval tehdy právě Scrypted. Kombinace levné šasi + trvalá inference 24/7 + doložitelně marginální chlazení je konzistentní s tepelným/VRM úmrtím. Dokázat to nejde, protože teplotu nikdo neměřil — a přesně to je důvod, proč monitoring zavést.

Rozhodnutí

  • Zavést teplotní monitoring pmx1 + pmx2 (lm-sensors → Grafana, alert na trvale >85 °C). Zapsáno do todos.md.
  • Fyzická údržba pmx1 (prach, teplovodivá pasta) je doporučená, ale až po zavedení monitoringu — jinak nebude s čím porovnat, jestli zásah pomohl. Postup: nejdřív neinvazivně profouknout a ověřit, že ventilátor točí; teprve pak otevírat, a jen s pastou po ruce. Časovat na klidnou chvíli (dům běží — klenoty jsou na pmx2 — ale pmx1 je DR cíl a nese PBS).
  • Ponaučení pro budoucí rozmisťování: trvalá CPU zátěž (AI inference, transcoding) nepatří na levné no-name šasi bez měření teplot. Pokud pmx1 i po vyčištění zůstane výrazně tepleji než pmx2 při srovnatelné zátěži, jde o konstrukční limit chlazení → zvážit, jestli má takový stroj držet DR roli celého domu.

2026-07-24 — QDevice arbiter na TrueNAS živý; ha-manager odemčen

Rozhodnutí a provedení

Dokončen QDevice (3. quorum hlas) pro 2-node cluster — rozdělaný Incus CT qnetd na TrueNAS dotažen do provozu:

  • Statická IP 192.168.20.23 natvrdo v kontejneru (systemd-networkd) jako autoritativní zdroj, + DHCP rezervace na pfSense pro úplnost přehledu. Konvence: kritická infra (pmx nody, quorum device, switche) = statická IP v zařízení; pfSense rezervace jen jako dokumentace/pojistka. Nody i qnetd tak nezávisí na DHCP při bootu.
  • corosync-qnetd + sshd na qnetd, root klíče pmx1/pmx2 → pvecm qdevice setup 192.168.20.23 -f.
  • Výsledek: expected votes 2 → 3, Flags: Quorate Qdevice.

Dopad

  • Kterýkoli PVE node teď může spadnout bez ztráty kvóra (přeživší 1 + qdevice 1 = 2 ze 3). Tím padá blocker rebootu pmx1 (dist-upgrade) i prerekvizita ha-manageru.
  • Caveat: arbiter běží na TrueNASu. Když spadne TrueNAS sám, nody drží 2/2 = OK. Ztráta kvóra hrozí jen při dvojitém výpadku (TrueNAS + jeden node současně) — přijatelná hrana.
  • Proč to nezavádí kruhovou závislost na pfSense: corosync i qdevice komunikují po L2 na VLAN 20 (přímo mezi nody + arbiter), nerouteují se přes pfSense; nody i qnetd mají statické IP. Výpadek pfSense (VM 100) proto neshodí kvórum — to je zároveň důvod, proč je qnetd staticky.

Follow-up: ha-manager (odemčeno)

Zapnout ha-manager pro pfSense (100) + HA (102) + debian-services (107) je teď proveditelné. Chce: ověřit funkční watchdog/fencing na obou nodech, ověřit replikaci 107, přijmout RPO = poslední replika (à 90 min, pro tyto workloady OK) a ~2–3 min automatický failover. Řeší přesně failure mode z 2026-07-05 (náhlá smrt nodu → dnes ruční DR). Samostatná řízená akce vč. failover testu.

2026-07-24 — PBS zůstává VM v clusteru (re-potvrzeno živými daty); TrueNAS jako kandidát na kompletní HW výměnu

Re-potvrzení rozhodnutí z 2026-07-06

Otázka „přesunout/replikovat PBS na TrueNAS" znovu zvážena — rozhodnutí „PBS zůstává VM v clusteru" platí, živá data ho posilují:

  • Data PBS už na TrueNASu jsou — datastore tank/pbs-data (275 G) leží na TrueNAS datasetu; VM 105 je jen výpočetní skořápka, co ho mountuje. Na datové úrovni tedy není co „přesouvat", a kruhová závislost je už rozbitá: cluster umře → zálohy na TrueNASu přežijí → PBS se rozjede kdekoli a datastore připojí (viz DR postup).
  • TrueNAS nemá RAM nazbyt (hůř než v červenci): 7,6 GB total, 588 MB free, ARC uškrcený na 1,1 GB. 4GB PBS VM by ARC rozdrtila a degradovala všechny storage služby (NVR, NFS, Immich media, i PBS datastore). Přesně ta ARC starvation z 2026-07-06.
  • QDevice + ha-manager to otáčí: PBS jako cluster VM je teď bezpečnější (auto-failover), ne rizikovější.

TrueNAS HW

Uživatel zamítl RAM upgrade TrueNASu (NUC8i3BEH, 8 GB/2C-4T) ve prospěch úvahy o kompletní nové TrueNAS krabici. Zapsáno jako otevřená úvaha (todos) — memory-tight stav (588 MB free, ARC 1,1 GB) je i tak signál, že current box je na hraně. Breakpoint pro PBS-na-TrueNAS se otevře až s výrazně větší RAM; do té doby PBS zůstává v clusteru.

2026-07-24 — HW roadmapa: konsolidovaný reshuffle (cíl H1 2027, hradlem je cena RAM)

Nahrazuje dřívější dílčí úvahy (náhrada pmx1, nový TrueNAS) jedním plánem. Cíl uživatele: udělat to celé do ~půl roku, aby byl fleet čistý. Trigger fáze 2 = návrat cen RAM.

Kontext: RAM/NAND krize (mid-2026)

DDR5 +110 %, chipy ~4× dráž, vrchol teď, návrat cen až Q4 2027+. Konkrétně (Mironet, 2026-07-24): Crucial 32GB (1×32GB) DDR5-5600 SODIMM = 8 863 Kč a vyprodáno — jeden modul = cena celého mini PC. Ověřeno, že žádná krabice ve fleetu nemá DDR5 k přesunu: pmx1 = 2×16GB DDR4-3200, TrueNAS = 8GB DDR4, pmx2 má DDR5 ale v provozu. → jakýkoli nový node s DDR5 je teď fakticky nekoupitelný/předražený.

Fáze 1 — teď (RAM krize): nic s DDR5

  • Thermal pmx2 (Scrypted, 72–82 °C, alert 88, Tjmax ~95 — monitorováno, ne havárie) řešit buď USB Coral (~2000 Kč, nula RAM), nebo jen jet na Grafana monitoringu. USB Coral pro Scrypted není dev-preferovaný (TF-Lite plugin, „lesser performance"), ale na 6 kamer stačí; je to jediná cesta, co neplatí vrchol cen RAM.
  • Žádné node nákupy.

Fáze 2 — cíl H1 2027 (až RAM zlevní): reshuffle

  • M1 Plus (Minisforum, Mironet ~8 837 Kč barebone, 3letá záruka): i5-12600H 12C/16T, Iris Xe (silný OpenVINO target → Scrypted detekce na iGPU = Scrypted-ideál, Coral tím odpadá), 2× DDR5 SODIMM až 96 GB (nepájená), 2× 2.5GbE, 1× M.2, USB4/WiFi6E → nový pmx1. Kup barebone; DDR5 (Crucial 32GB, ~8 863 Kč — začít 1×32, druhý slot na později) až v 2027. Tradeoff: i5-12600H je 45W třída (teplejší než N100), ale detekce na iGPU drží CPU load nízko + je to řádně chlazený kus + monitoring teplot už běží.
  • Starý pmx1 (Ryzen 5 7430U, 32 GB DDR4) → nový TrueNAS. Jeho 32 GB (vs. dnešních 8) opraví ARC starvation; jako NAS (nízký CPU) poběží chladně → thermal strach mizí. DDR4 sedí.
  • Disky: archive (4TB WD Purple) je na USB → triviálně přepojit na nový TrueNAS. tank (1TB Intel 660p NVMe) → do M1 Plus jako rpool; POZOR: tím zaniká pool tankpředem naplánovat migraci PBS datastore (tank/pbs-data, 275 G) + app-data (nginx/grafana/ loki NFS) + VM storage. To je jádro celé operace, ne swap disku.
  • Retire NUC8i3BEH (8GB, nejslabší článek).
  • Nekupuje se disk ani nový TrueNAS box — reuse existujícího HW obchází i nafouknuté ceny NAND.

Zamítnuté varianty (kontext)

Nvidia (jen kvůli LLM, ten jde na samostatný Mac Mini); Lunar Lake / MSI Cubi NUC AI (RAM v pouzdře, 32 GB strop); N100/N-series jako pmx1 (1× SODIMM nebo slabý CPU — není peer pmx2); RAM upgrade současného TrueNASu (řeší se reshuffle-em zdarma); PBS na TrueNAS (viz samostatný záznam výše).

2026-07-24 — Směr náhrady pmx1: Intel Arc iGPU (ne Nvidia, ne Lunar Lake); LLM na samostatný Mac Mini

Poznámka 2026-07-24: konkretizováno a nahrazeno záznamem „HW roadmapa: konsolidovaný reshuffle" výše (M1 Plus = ten Intel iGPU box; Coral jen fáze 1). Tento záznam ponechán pro kontext úvahy.

Kontext

Diagnostika #87 odhalila, že pmx1 běží horko (81 °C, no-name kus, sesterský kus umřel) a že detekce dře na CPU (OpenVINO na AMD neakceleruje). Otevřela se otázka náhrady pmx1 za stroj s GPU akcelerací.

Rozhodnutí (směr, ne konkrétní kus)

  • Intel s Arc iGPU je zvolený směr. Pro tenhle workload mix (Scrypted detekce + Immich ML + Jellyfin transcode) vyhrává nad Nvidia: nativní OpenVINO (Scrypted detekce s nulovou migrací — přesně to, co je na AMD slepá ulička), QuickSync pro Jellyfin, OpenVINO i pro Immich ML, in-kernel i915/xe driver bez DKMS (stejný argument, proč jsme zavrhli M.2 Coral), passthrough přes renderD128 jako dnešní iGPU.
  • Nvidia zamítnuta. Jediná osa, kde vyhrávala, byl lokální LLM (CUDA). Ten padá na samostatný Mac Mini (Apple Silicon je na LLM efektivnější a odděluje compute od kritické infra) → z úvah o pmx nodu LLM mizí, a s ním důvod pro Nvidia (DKMS driver, větší/hlučnější skříň, migrace Scryptedu na ONNX/CUDA).
  • Lunar Lake (MSI Cubi NUC AI, Core Ultra 7 258V) zamítnut — RAM v pouzdře CPU, 32 GB strop navždy (už zamítnuto 2026-07-06 pro klenoty; pro workhorse+DR roli je to bez LLM sice mírnější, ale nulová trvalá rezerva a žádný upgrade = zbytečné riziko). Preferovat model s SODIMM (Meteor/Arrow Lake mini PC s Arc iGPU) → cesta na 64 GB. NPU Lunar Lake není potřeba — na OpenVINO detekci stačí Arc iGPU.

Priorita a hranice

  • Není urgentní. #87 je vyřešený přesunem Scryptedu na pmx2; detekce tam běží na CPU s rezervou. Pořadí kroků: repasta/vyčištění pmx1 ≫ (případný Coral USB jen na detekci) ≫ nový box.
  • Coral vs box = falešná volba — řeší různé problémy. Coral (USB, ~1500 Kč) sundá jen detekci z CPU, neřeší teplo pmx1 ani transcode/Immich. Box řeší vše, ale je to projekt + výdaj. Kupovat teď ani jedno — detekce netlačí, pmx1 nejdřív repasta.
  • Konkrétní kandidáti + živé ceny → samostatná storm-research session (zapsáno v todos).

2026-07-23 — Klima: idle ventilátor se nechává běžet; „fan OFF při thermo-off" neprůchodné

Kontext

Otázka: když Daikin po dosažení setpointu odpojí kompresor (thermo-off), vnitřní jednotka dál točí ventilátorem (míchá vzduch + měří return-air teplotu). Přání bylo ventilátor v tu chvíli úplně zastavit — primárně kvůli hluku.

Zjištění

  • Fan-off při thermo-off je na Daikinu field setting jednotky, ne runtime registr. Náš Modbus gateway (EKMBDXA/EKMBDXB) exponuje jen runtime (on/off, mode, setpoint, rychlost/směr ventilátoru) — field settings v mapě nejsou (ověřeno proti manuálu 4P357732-1).
  • Field setting nejde pohodlně změnit. Všech 10 jednotek je vestavěných (FXSQ podhledové, FXNQ parapetní) a žádná Madoka není nainstalovaná — jediná ovládací plocha je HA přes Modbus. Změna field settingu by znamenala buď otevírat zabudované jednotky k PCB, nebo instalatérem dočasně nabusovat ovladač na F1/F2 každé jednotky. Není to zásah „na zavolání".
  • Fan-off by navíc oslepil return-air čidlo jednotky (stojatý vzduch pod stropem) → vynutil by si externí čidlo pro řízení. Wall-thermostat řízení jde jen softwarovým bang-bangem v HA.

Rozhodnutí

  • Klima control se nechává as-is — jednotky v thermo-off drží ventilátor běžet (záměr výrobce; na modulačním VRV je to šetrnější než softwarové power-cycling, které maří inverter modulaci a přidává cyklovací opotřebení + overshoot přes anti-short-cycle timery outdooru).
  • Softwarový bang-bang (generic_thermostat + power switch + nástěnný termostat) se nestaví. Zvažováno, zamítnuto: energeticky se nevyhraje (úspora = jen ventilátor, řádově desítky W/jednotka), na VRV kontraproduktivní. Kdyby se přání vrátilo, prototypovat na jedné jednotce (ložnice), ne plošně.
  • Jediná dostupná páka na hluk = rychlost ventilátoru přes Modbus, a ta je už vytažená naplno — fan speed 1 všude a natrvalo. „Nechat as-is" tedy není rezignace, ale stav bez dalšího dostupného kroku.

Follow-up (jen kdyby)

Kdyby u jednotek někdy byla Madoka na sběrnici (servis, rozšíření), fan-off field setting se stává proveditelným zadarmo — dá ticho bez cyklování. Do té doby neřešit.

2026-07-22 — DALI tlačítka: kanonická je vrstva spárovaných vypínačů v komponentě; ButtonManager končí

Zjištění

Dokumentace tlačítek žila ve dvou rozporných světech: provozní stránky popisovaly AppDaemon (FoxtronSceneController + ButtonManager) jako současnost, pět design dokumentů popisovalo nikdy nerealizovanou ButtonManager/Room vizi — a skutečný stav (custom komponenta ha-foxtron-dali skládá gesta, páruje fyzické vypínače jako HA zařízení s device triggery a eventem foxtron_dali_button_action) nebyl zdokumentovaný vůbec. Audit komponenty navíc odhalil, že foxtron_dali_button_action se kvůli bugu v parsování mapování instancí reálně nikdy nestřílel (opraveno 2026-07-22 v repu komponenty) — proto si nikdo nevšiml, že device triggery nefungují.

Rozhodnutí

  • Kanonická identita vypínače = spárované zařízení v HA registru (komponenta, identifier dali4sw_<bus>_<addr>_<up>_<low>), events foxtron_dali_button_action (device_id/flap/press_type) pro HA automatizace, legacy foxtron_dali_button_event zůstává pro AppDaemon.
  • AppDaemon button_manager (+ buttons.yaml, check_buttons.py) se odstraní — jediný konzument (zvonek, dvojklik nahoře → otevření branky) se přepne na nativní event; do té doby běží obojí. ButtonManager/Room design dokumenty přesunuty do docs/chytry_dum/archiv/ s bannerem.
  • FoxtronSceneController zůstává — živá logika světel, v souladu s rozhodnutím 2026-06-17 (AppDaemon zachovat). Migrace scénové logiky do HA automatizací není na stole.
  • Provizorní spárovaná zařízení přejmenována (Vypínač <Místnost> <upřesnění>) a přiřazena do areas; nalezen duplikát WC (spárován 2× ve starém i novém formátu) a jeden neidentifikovaný vypínač (bus 21_23, addr 1) — viz todos.

2026-07-22 — Floorplan: pojmenované SVG polygony jako geometrický zdroj pravdy, pozice v dashboardu jako zdroj pravdy rozmístění

Rozhodnutí

  • Půdorysy = čisté SVG (www/floorplan/1np.svg, 2np.svg), každý polygon místnosti nese id (unikátní prostor) a data-area (HA area). Generuje scripts/floorplan_build.py z Inkscape overlay zdrojáků (~10 MB, drženy mimo repo — skript je bere jako argument). Rozdíly polygon vs. area vědomě: bazen_wc/bazen_technologie → area bazen (hrubý area model dle 2026-06), schodiste (2NP) → area hala.
  • Pozice prvků v dashboards/pudorys.yaml (picture-elements, top/left %) = zdroj pravdy fyzického rozmístění světel, rolet a vypínačů. Slouží dashboardu i budoucí blízkostní analýze (vypínač→roleta: primárně stejná area/polygon, blízkost jen jako disambiguace uvnitř místnosti). Vypínače v dashboardu jsou pasivní ikony s title = jméno spárovaného zařízení (nejsou to entity).

2026-07-20 — Celodomovní blokování reklam: Unbound forwarding přes DoT na AdGuard DNS (+ DNS4EU fallback)

Zjištění

Unbound na pfSense běžel jako čistý rekurzivní resolver — DNS4EU servery (86.54.11.211/.11, filtrovaná varianta) zapsané v General Setup se pro klientské dotazy vůbec nepoužívaly (žádná forward-zone). Dům tedy do této změny neměl žádné DNS blokování reklam, přestože konfigurace vypadala, že ano. Docs (firewall.md) navíc spekulovaly „pravděpodobně Cloudflare" — nikdy neověřeno proti živému stavu.

Rozhodnutí

Unbound přepnut do forwarding módu s DNS-over-TLS (ověření certifikátu přes TLS hostname):

  • AdGuard DNS Default 94.140.14.14 + 94.140.15.15 (dns.adguard-dns.com) — primární, blokuje reklamy a trackery. Vědomě zvolena Default varianta (ne Family) — platí pro celý dům.
  • DNS4EU Protective + Ad Blocking 86.54.11.13 (noads.joindns4.eu) — třetí upstream jako nezávislý evropský fallback se stejnou politikou.
  • DNSSEC ponechán zapnutý (oba provideři podporují). Host overrides *.marada.name forwarding neovlivňuje.

Trade-offy

  • Fallback není strict primary/backup — Unbound RTT-balancuje mezi všemi třemi upstreamy, část dotazů jde na DNS4EU i za normálu. Akceptováno: oba blokují reklamy, výpadek jednoho providera dům nepoloží. forward-first (auto-návrat k rekurzi) pfSense GUI nepodporuje — custom-options hack zamítnut (KISS).
  • False positive filtru = rozbitá stránka; řešení: Host Override, nebo vypnout Enable Forwarding Mode (návrat k nefiltrované rekurzi). Bez self-hosted AdGuard Home není per-doména whitelist — vědomě přijato, žádná nová služba.
  • Zařízení s hardcoded DNS (typicky IoT) filtr obcházejí — follow-up v todos.

Provedení

Config zapsán přes SSH + PHP (config_set_pathwrite_configservices_unbound_configure); automatický backup v config history (Diagnostics → Backup & Restore) = rollback cesta. Ověřeno: forward-zone s @853#hostname, aktivní TLS spojení na :853, doubleclick.net → 0.0.0.0, normální domény i lokální overrides OK. Při tom opravena chyba doc_gen_pfsense.py (boolean <forwarding> je prázdný tag — čte se přítomnost, ne hodnota).

2026-07-06 — Nový pmx2 v provozu: join, migrace klenotů, replikace 90 min

Rozhodnutí

Nový uzel (ASUS PN54-S1) nainstalován a připojen do clusteru jako pmx2 (192.168.20.3, PVE 9.2.4, ZFS rpool, ashift 12, ARC limit ~2,8 GB). Rozmístění: korunní klenoty pfSense (100) + HA (102) běží na pmx2 (novější/spolehlivější HW), pmx1 nese unifi/PBS/scrypted/immich (+win11 trvale off). Do budoucího ha-manageru půjdou jen pfSense + HA + debian-services (debian přidán ještě týž večer — nginx na něm nese externí přístup ha.marada.name); scrypted/unifi/immich vědomě mimo HA i mimo replikaci (PBS restore stačí — rozhodnutí uživatele). Malá HA množina = failover se vejde do RAM přeživšího uzlu (trojice na pmx1 je těsná ~29/30 G; nouzový ventil = stopnout Immich) a minimální fencing blast radius. Replikace: 100/102 pmx2→pmx1, 105 (PBS) pmx1→pmx2, vše à 90 min (ne 15 — RPO zisk je u tohoto workloadu kosmetický: HA po startu stejně re-polluje stav, pfSense config se skoro nemění; opotřebení disků s frekvencí nesouvisí, replikují se jen delty). Inkrementální sync potvrzen: 26 s.

Zjištění (limity infrastruktury)

  • USW-48-PoE je 1GbE — dual 2.5GbE porty na PN54 jsou bez 2.5G switche/přímého kabelu k ničemu.
  • SSH cipher strop ~60 MB/s (single-core) → replikace/migrace reálně ~30 MB/s. Mitigace k zvážení: migration: type=insecure v datacenter.cfg, přímý kabel, nebo nechat být (delty jsou malé). Viz todos.
  • Verze: no-subscription repo dal 9.2.4, pmx1 má 9.1.9 — join v rámci major 9 OK; pmx1 se dožene bezvýpadkově (klenoty teď běží na pmx2, reboot pmx1 ničemu nevadí).
  • Kvórum: expected 2 → smrt uzlu = ztráta kvóra, dokud není QDevice. QDevice je proto prerekvizita ha-manageru, ne nice-to-have.

Lekce z migrace (agentův fail)

qm migrate zdědil bwlimit 10 MB/s ze stale replikačního jobu → vypadal zaseklý; agent ho chybně prohlásil za mrtvý a smazal rozpracovaný disk na cíli → migrace odsouzena, nutný čistý restart. Postup příště: u „zaseklé" migrace nejdřív ps (zfs send/recv, cstream throttle) a změřit progress (zfs get referenced 2× po sobě), teprve pak zasahovat. Dlouhé migrace pouštět nohup ... & na uzlu, ne přes drženou SSH session. Druhý pokus: „46 GB" byla iluze (thick rezervace + snapshoty; reálná data 8,6 G), downtime 32 ms (HA) / neznatelný (pfSense).

Operační dopad

  • vzdump job 21:00 automaticky následuje guesty po migraci — ověřeno: denní řada záloh nepřerušená vč. dne incidentu.
  • Storage zůstává thick (thin zamítnut: overcommit=freeze-pool riziko výměnou za úsporu, kterou při 18% zaplnění nepotřebujeme).
  • Mosquitto běží jako HA add-on (192.168.20.7:1883), ne na debian-services — opravena stale informace v memory.
  • Immich: z mrtvého pmx2 přežily jen fotky (7438 ks / 2,6 G, TrueNAS archive/stash/immich-import); DB/metadata pryč — akceptováno. Nový CT 104 na pmx1 = experiment: fresh Docker deploy (v3.0.1), vědomě bez replikace a mimo backup job.

2026-07-06 — PBS zůstává VM v clusteru (TrueNAS hosting zamítnut)

Rozhodnutí

PBS (105) zůstává jako VM v Proxmox clusteru se ZFS replikací na druhý uzel. Přesun na TrueNAS (řešil by kruhovou závislost zálohovacího serveru na zálohovaném clusteru) zamítnut.

Důvod

TrueNAS box (NUC8i3BEH) má jen 8 GB RAM a 2 jádra — 4GB PBS VM by vyhladověla ZFS ARC. Kruhová závislost je dostatečně mitigována replikou PBS na druhý uzel (incident 2026-07-05 potvrdil: PBS obnoven z repliky během minut) + datastore na TrueNAS přežívá smrt clusteru tak jako tak. Na TrueNASu zůstává rozdělaný qnetd kontejner pro QDevice (viz todos).


2026-07-06 — Náhrada pmx2: ASUS ExpertCenter PN54-S1 (S70016AL)

Rozhodnutí

Mrtvý pmx2 (no-name mini PC, Ryzen 5 7430U) nahradí ASUS ExpertCenter PN54-S1-S70016AL — Ryzen 7 260 (Zen4), 32 GB DDR5 SODIMM, 1TB NVMe, dual 2.5GbE, ~24 000 Kč.

Důvod

Vybíráno z ~10 kandidátů. Rozhodly parametry podstatné pro virtualizační uzel, ne špičkový CPU výkon: 2× SODIMM (cesta na 64 GB — po výpadku musí jeden uzel občas unést všechno), dual 2.5GbE, 2× M.2 (možnost ZFS mirror), AMD (homogenita s pmx1 pro cpu: host migrace) a business QC/servis po zkušenosti s umřelým no-name kusem. Zamítnuto: Beelink SER9/SEi13 (pájená RAM / starý mid-range Intel, předražené), Lenovo IdeaCentre 01Q8X10 (Snapdragon = ARM, Proxmox x86-only), Acemagic F1A (QC, malware skandál 2024), MSI Cubi NUC AI+ i ASUS NUC 14 Pro AI (Lunar Lake = 32 GB RAM on-package bez upgradu), PN54 s Ryzen AI 7 350 (+9k za NPU/Zen5, které headless uzel nevyužije).

Operační dopad

Po doručení: PVE 9 + ZFS, join do (nyní jednouzlového) clusteru, obnova replikací, návrat VM. Migrovatelným VM nastavit cpu: x86-64-v3cpu: host migrace Zen3→Zen4 projde jen jedním směrem. Kroky v todos.md.


2026-07-06 — Výpadek pmx2 (HW smrt) — obnova HA/PBS z ZFS replik na pmx1

Příčina

pmx2 (mini PC) 5. 7. ve 23:32 náhle zhasl — v syslogu žádná shutdown sekvence, jen konec běžné replikační SSH session (23:30) a pak ticho. Test výměnou napájecího adaptéru z pmx1 zdroj vyloučil → mrtvý hardware (deska/„něco uvnitř").

Důsledky

Down: HA (102), PBS (105), Immich (104), Scrypted (106), Jellyfin (110). Cluster ztratil kvórum (2 reálné uzly → 1 hlas ze 2), pmxcfs na pmx1 read-only. Druhá vlna 6. 7. ráno: test zdroje = reboot pmx1 → pvecm expected 1 reboot nepřežije → bez kvóra nenastartovala žádná VM včetně pfSense → celý dům bez sítě, nutný zásah z fyzické konzole.

Oprava

  1. pvecm expected 1mv configů 102/105 z /etc/pve/nodes/pmx2/qemu-server/ na pmx1 → start z ZFS replik. Poslední replika 23:30:05, crash 23:32 → RPO ~1 minuta (šťastné načasování 90min intervalu).
  2. Replikační joby 100-0/102-0/105-0 disabled (cíl mrtvý, spamovaly by chyby).
  3. pvecm delnode pmx2 → jednouzlový cluster, expected=1 trvale, reboot přežije. Configy LXC 104/106/110 zálohovány na pmx1: /root/pmx2-configs-backup-20260706.

Lessons / follow-up

  • DR runbook měl díru: předpokládal jen qm start z repliky, ale bez kvóra je pmxcfs read-only a config VM „patří" mrtvému uzlu. Doplněno do disaster_recovery.md (kvórum + mv configu).
  • pvecm expected 1 není perzistentní — po každém rebootu znovu, dokud je cluster degradovaný. Trvale řeší až delnode (teď) / QDevice (budoucnost).
  • Dvouuzlový cluster ztrácí kvórum při výpadku kteréhokoli uzlu → po stavbě nového uzlu přidat QDevice (corosync-qnetd) na TrueNAS (todos).
  • ZFS replikace se plně osvědčila (RPO ~1 min, RTO ~10 min) — po obnově clusteru vrátit a přidat i pro UniFi VM 109 (todo existuje).
  • Immich/Scrypted/Jellyfin repliku neměly → obnova z PBS (datastore na TrueNAS přežil, PBS běží).

2026-06-24 — Klima dashboard: master tlačítka okna + presety stínění + rename 2 žaluzií

Rozhodnutí

Na Klima dashboard přidána master tlačítka Otevřít vše / Zavřít vše pro 10 oken (inline cover.open_cover/close_cover, seznam entit přes YAML anchor &okna_vse) a dva presety stínění jako skripty script.stineni_dopoledni / script.stineni_odpoledni s tlačítky nad „Stínění 1NP". Presety posílají nejdřív pozici, pak tilt:

  • Dopolední: kuchyně + Relax sedací + Relax dřevník → pozice 0; jídelna → pozice 10 (90 % zavřeno); všechny 4 → tilt 70.
  • Odpolední: Vrba + Hala schody + Koupelna → pozice 0 + tilt 70.

Zároveň přejmenovány dvě cover entity (registry, friendly name beze změny): cover.relax_zaluzie_drevnikcover.hala_zaluzie_vrba (friendly „Vrba", area hala) a cover.relax_zaluzie_portalcover.relax_zaluzie_drevnik (friendly „Relax dřevník", area relax).

Důvod

Okna neměla v entities kartě šipku „zavřít" → master tlačítka problém obejdou (servisní volání funguje bez ohledu na UI). Stará entity_id byla zavádějící: entita s _drevnik byla fyzicky Vrba u terasy a _portal byl Relax dřevník — nesedělo to na umístění. Přejmenováno teď, dokud byla jediná reference (klima.yaml); blast radius minimální, historie v recorderu se při rename zachovává (HA 2022.4+).

Gotcha — HA 2026.6.x skrývá „zavřít" u device_class: window

Cover entity-row v entities kartě nezobrazí tlačítko zavřít (šipka dolů) pro device_class: window, i když entita CLOSE plně podporuje (supported_features 15). U shutter/blind se zobrazí — proto stínění šipku dolů má a okna ne. Ověřeno controlled experimentem (window vs shutter, oba sf=15, liší se jen device_class) i živě na 2026.6.x. V dev větvi HA frontendu už tahle logika není → regrese konkrétní verze. Workaround pro per-okno šipku by byl override device_class na shutter; zvoleno místo toho master tlačítka (KISS, sémanticky čisté).

Operační dopad

  • Somfy IO (overkiz) zabíjí probíhající pohyb následným příkazem — potvrzeno 2026-06-24: při odpoledním presetu koupelna sjížděla ze 100, tilt příkaz ji zrušil → nesjela. Fix: mezi set_cover_position a set_cover_tilt_position je wait_template, který čeká na dosažení cílové current_position (skupina = 0, jídelna = okno 8–13, protože Somfy nehlásí přesných 10), timeout: 120 + continue_on_timeout: true jako pojistka. Čeká se na pozici, ne na stav closing — odolné vůči latenci overkiz (žaluzie drží výchozí pozici dokud nedojede, wait nepředběhne).
  • Pozice se posílá jen žaluziím, co ještě nejsou na cíli (2026-06-24): close na už zavřenou Somfy IO žaluzii ji stejně rozjede a sklopí tilt na 0 (zbytečný pohyb + flicker). Skript proto přes variables: zavrit spočítá podmnožinu (expand | rejectattr current_position == 0) a set_cover_position pošle jen jí; jídelna analogicky if na okno 8–13; tilt 70 se pak pošle všem. Druhý stisk presetu na už zastíněné žaluzie → žádný pohyb pozice, jen (případně) tilt.
  • Skripty mode: restart → opakovaný stisk čistě přepíše běžící preset.
  • Staré body v InfluxDB zůstaly pod starým entity_id (u žaluzií irelevantní, žádný Grafana panel je nepoužívá).

2026-06-24 — Workflow paralelních session: worktrees + ruční merge sdílených logů

Rozhodnutí

Paralelní agent session jedou každá ve vlastním git worktree (izolace), ne sdíleně nad jedním master checkoutem. decision_log.md a todos.md zůstávají jako jednotlivé soubory (žádný per-entry split). Konflikt z paralelních zápisů se neřeší automaticky (žádný merge=union driver) — agent ho integruje ručně se správným newest-first pořadím. Toto chování je nově zakotvené v commit-confirmation.md (sekce „Append-only sdílené logy" + krok 5b), aby ho dělala každá session spolehlivě, ne náhodou.

Důvod

Paralelní session sdílející jeden master checkout je nebezpečné: git commit v jedné sebere i rozdělanou práci druhé (společný index) + race na sdílených souborech. Worktree to izoluje. Per-entry split (docs/decisions/*.md + generátor) konflikt řeší u zdroje, ale přesouvá ho na generovaný soubor a přidává infrastrukturu — zamítnuto kvůli KISS a přání zachovat jeden soubor. merge=union je čistě KISS, ale slepuje text hloupě (rozhází date pořadí, neumí dedup) — zamítnuto ve prospěch vědomého agent-merge, který dá korektní výsledek. Cena: vyžaduje agenta v procesu commitu, což sólo provoz stejně má.

Operační dopad

  • Nikdy nemíchat režimy — když jedu worktrees, master checkout se ručně needituje (je to jen push target). Mixování bylo příčinou divergence zahradni_domek.md (744 vs 691 řádků ve dvou kopiích).
  • Před pushem commitu dotýkajícího se decision_log.md/todos.md: git fetch origin master, integrovat pokud se rozešel, ručně ověřit pořadí. Viz commit-confirmation.md.
  • Worktree se po session uklízí (git worktree remove + git branch -D); čisté worktree = 0 commitů navíc, bezpečné smazat. 2026-06-24 uklizeno 6 nahromaděných.
  • EnterWorktree/ExitWorktree nefungují na worktree vytvořený harnessem při startu session — z něj se ven dostat nedá, je potřeba session spustit přímo v main repu (app-level volba, ne settings.json).

2026-06-22 — FTV export módy (Mode 1/3 přes Victron grid setpoint) + výdělek z dodávky

Rozhodnutí

Ruční přepínač dodávky do sítě řízený přes Victron ESS grid setpoint (HACS victron_mqtt, zapisovatelné number entity na Cerbo GX). input_boolean.ftv_max_export: OFF = Mode 1 (samospotřeba, setpoint 0), ON = Mode 3 (setpoint −12500 → export 12,5 kW, vybíjí baterku do floor input_number.ftv_export_min_soc, default 20 %; přetok >12,5 kW → baterka). Páky: …ac_power_set_point (0/−12500), …ess_max_feed_in_power (12000 strop), …ess_min_soc_limit (floor). Plus počítadlo výdělku z dodávky (sensor.vykup_vydelek_dnes, Kč) přes Riemann integraci export×výkupní cena.

Klíčové zjištění — Mode 2 je nativně nedosažitelný

Záměr „export PV první, NIKDY nevybíjet, jen přetok >12,5 do baterky" v nativním ESS nejde: ESS vždy upřednostní nabití baterky před exportem; jediná páka co vynutí export-first je záporný grid setpoint, který ale vybíjí baterku (= Mode 3). Discharge-blok přes min SoC=100 je anti-pattern — spustí low-battery ochranu = grid-charge (nákup fixu) na 100 %. Workflow ověření (Victron ESS dokumentace + adversariální review) to zachytilo před zápisem do YAML. Zůstaly 2 čisté módy přes input_boolean.

Bezpečnostní obálka (perzistentní setpoint na Cerbu = pád HA nesmí nechat Mode 3 přes noc)

  • ftv_export_apply: number.set_value, kanonické pořadí (do Mode 3 floor→setpoint; návrat setpoint→0 první), read-back setpointu + notifikace při neshodě, Mode 3 vylučuje ftv_tc_boost.
  • ftv_export_guard (à 10 min): vypne při výkupu (OTE−0,30) ≤ práh (default 0 = nikdy se ztrátou).
  • ftv_export_reset: 23:00 + homeassistant.start → Mode 1, s wait_template na dostupnost victron entit.
  • Clamp hodnot (range entit je ±1 000 000, žádná knihovní pojistka).

Flip-test (živě, 2026-06-22, PV 15 kW / SoC 90 %)

Mode 3 ON → setpoint −12500, grid −12257 W (export ~12,3 kW, pod limitem 12,5), baterka z +6,6 kW nabíjení → ~0, ftv_tc_boost OFF (mutual exclusion). Revert → setpoint 0, grid ~0, baterka +13 kW. Celý řetězec ověřen.

Cenové prahy (kdy ON)

Výkup = OTE − 0,30 (marže EnerSpot, ne distribuce — ta se na exportu neplatí). Práh „neprodávej se ztrátou" = OTE 0,30. Mode 3 vybíjí baterku (~1,2 Kč/kWh opotřebení) → reálně dává smysl až od OTE ≳ 1,5. Ranní prodej s jistým odpoledním dobitím z mařeného přebytku: break-even ~OTE 1,5; bez té jistoty se ranní prodej obchoduje proti večernímu nákupu (práh skočí na OTE ≥ 4,28). Detail rizeni_energie.

Operační dopad

Nasazeno (input_boolean.yaml, input_number.yaml, automations.yaml, template.yaml, sensor.yaml, configuration.yaml, dashboards/ftv.yaml), commity 14f495e + 230c478, restart (sensor.yaml + utility_meter). Dashboard: přepínač/rezerva/práh/setpoint + spot graf dnes+zítra + výdělek dnes. Rodina: navod_pro_rodinu/energie — do Victron GUI nesahat, přepínat na dashboardu.


2026-06-22 — AK je v létě dead-end sink (kompresor nehřeje + VZT neodebírá)

Zjištění

Akumulačka AK je v létě slepá ulička pro přebytek, ze dvou důvodů současně:

  1. Kompresor ji nenatopí — topná služba je gated globálním stop-temp (léto venku >20 °C → binary_sensor.ecoforest_heating_mode_active = off). Známo od 2026-06-21.
  2. VZT z ní v létě neodebírá — jediný letní možný konzument tepla z AK je VZT ohřívač bazénové haly (okruh DG1, dostává vodu ≈ teplota AK, otevírá ventil sám na svůj požadavek). V létě hala teplo nepotřebuje → ventil neotevře. Ověřeno daty (2026-06-22): po natopení patronou na 40,6 °C AK monotónně chládne ~1,2 °C/14,5 h = ~38 W standing loss (čistá ztráta nádrže, žádný odběr; reálný odběr 2 kW by srazil ~4 °C/h).

Důsledek

Patrona do AK v létě = zmařená elektřina (COP 1, teplo se nespotřebuje, vyteče do strojovny). AK boost (kompresor i patrona) má smysl jen v topné sezóně. Doc rizeni_energie tvrdila „z AK celoročně topí VZT ~2 kW v noci" — opraveno na topnou sezónu.


2026-06-21 — Bazén: FTV boost ohřevu (Aseko floor <28 / boost 28-32 / cap 32)

Rozhodnutí

Bazén = velký letní surplus sink (39 m³ ≈ 45 kWh/°C). Řízení ohřevu konsolidováno do jediné automatizace „Bazén - řízení topení" (automations.yaml, nahradila prostý Mirror Aseko→switch.topeni_bazen):

  • < 28 °C: topí Aseko (komfort floor, binary_sensor.topeni_aseko).
  • 28–32 °C: topí navíc při input_boolean.ftv_bazen_boost (přepínač na dashboardu) — jímání FTV přebytku.
  • > 32 °C: cap (hystereze 31,5/32). Topí kompresor TČ (COP), bazén z AK neodebírá (samostatná větev z kondenzátoru — projekt Veskom). Druhá automatizace „Bazén - filtrace při FTV boostu" vynutí switch.filtrace_bazen po dobu boostu.

Klíčová designová zjištění

  • Sonda bez filtrace měří vzduch, ne vodu → boost topí až po ≥15 min běhu filtrace (settle vázán na filtraci, ne na dobu boostu). Filtrace Aseko běží 8:00–20:00; FTV přebytek (plná baterka) nastává až dopoledne/v poledne → než boost najede, filtrace už hodiny běží, teplota spolehlivá. Settle je tedy v praxi vždy splněný, je to jen pojistka.
  • Dual-writer s Aseko vyřešen tím, že nová automatizace je jediný zapisovatel switch.topeni_bazen (kombinuje Aseko request i boost). Aseko cíl teploty doporučeno srovnat na 28 °C.
  • Aseko zápis: HA nikdy nepíše do dávkování/chemie Aseka; řídí jen relé ohřevu a (při boostu) filtrace.
  • COP pořadí (léto): kompresor → bazén + DHW (COP 3–4) → patrony (COP 1). Bazén před patronou ne kvůli odběru z AK, ale kvůli COP.

Operační dopad

Nasazeno (automations.yaml + input_boolean.yaml + dashboards/ftv.yaml), deploy reload. Dokumentace: bazen.md → „Ohřev bazénu a FTV boost". Entity_id přepsané automatizace zůstalo automation.mirror_binary_sensor_topeni_aseko_to_switch_topeni (HA drží registrované id při změně aliasu), friendly name „Bazén - řízení topení".


2026-06-21 — TČ surplus: AK/buffer kanál je v létě inertní (topná služba stopnutá stop-temp)

Zjištění (živě ověřeno během boostu)

Při boostu (DHW 60 / AK 48) DHW naskočilo a nahřálo se na 62 °C, ale AK se nehřeje (35,5 °C, 12,5 °C pod bus setpointem 48), TČ stojí (příkon 0 W). Příčina: venku 30 °C, binary_sensor.ecoforest_heating_mode_active = off — „Stop temperature for heating" je 5 °C, takže celý topný režim je globálně vypnutý. AK (topný buffer) je součást topné služby → bez aktivního topení se nehřeje a bus setpoint (2722) to neobejde (nastaví cíl, ale topná služba je stopnutá). DHW i bazén jsou samostatné služby (StopT je neovlivní) → fungují celoročně.

Důsledek / rozhodnutí

  • AK boost kanál = jen zimní. V létě nelze rozjet bez zvednutí globální stop-temp (společná pro dům i halu → roztopila by dům). Zápisy 2713/2722 v ftv_tc_boost zůstávají (neškodný no-op v létě, funkční v zimě).
  • Letní kompresorový surplus sink = DHW (400 l, plný za ~15 min) + bazén (39 m³, samostatná služba) → bazén je ta velká letní cesta. Patrony EP1/EP2 (COP 1) overflow.

2026-06-21 — FVE surplus přes kompresor: Path B ověřen a nasazen (manuální boost v1)

Zjištění (živý flip-test přes HA modbus.write_register)

Setpoint-by-BUS funguje — narozdíl od inertní e-Manager surplus funkce (2775/2777). Zápis 2705=1 (DHW demand by bus) + 2720=600 a 2713=1 + 2722=480:

  • sensor.ecoforest_dhw_setpoint 48 → 60, _heating_buffer_setpoint 30 → 48 (override se aplikoval),
  • _dhw_power 0 → 15,7 kW tepla, příkon 2,3 → 4,4 kW → COP ~3,5 = kompresor (ne patrona COP 1),
  • vypnutí přepínače → setpointy zpět 48/30, výkon 0. DHW teplota za 80 s beze změny (51,4 °C) → bez přehřátí.

Rozhodnutí

Cesta = Path B (HA řídí TČ setpointy přes BUS), surplus registry 2775/2777 opuštěny natrvalo. Nasazena minimální v1 (commit 129dc70): input_boolean.ftv_tc_boost (ON → DHW 60 / AK 48, OFF → 48 / 30) + automatizace ftv_boost_setpointu_tc_dhw_ak (+ re-assert na startu HA) + noční bezpečnostní reset ftv_nocni_reset_setpointu_tc_22_00 (22:00 → vypne boost, DHW 45 / AK 30). Zápis jen přes HA modbus.write_register (hub ecoforest, slave 17). modbus/ecoforest.yaml se nemění → deploy bez restartu.

Klíčový princip (od uživatele)

Setpointy zvedat dřív než nastane curtailment — jakmile je baterie plná, je pozdě (přetok max 12,5 kW, při záporné ceně nula). Proto teď manuální přepínač (zvednout ráno za slunce); automatika později vázaná na předpověď výroby, ne na curtailment signál.

Další (vše „později")

Prediktivní automatické zvedání (Forecast.Solar), patrony EP1/EP2 jako overflow (COP 1), bazén (Aseko koexistence), watchdog/age-check. Operační: před trvalým boostem na 60 sníží uživatel termostatický směšovač TV ze 75 na 55 °C (scald cap).


2026-06-21 — FVE surplus přes kompresor TČ: Fáze 0 — zapisovatelné, ale blokované na e-Manageru

Zjištění (živý write-test 2775/2777 + offsety, plně zarollbackováno)

  • Registry surplus jsou zapisovatelné a drží: 2775 (surplus enabled by BUS), 2777 (setpoint kW ×10), offsety 2725/2727. „Bus control" v menu je zapnutý.
  • Kompresor ale nenaskočí — ani s offsety dávajícími headroom (TV cíl 60 vs 52,5; AK cíl 48 vs 36,4) a 2777=4 kW. 150 s idle, compref 0 %, 0 kW.
  • Příčina (control manuál 3.2.7 e-MANAGER): surplus-by-BUS je součást e-Manager funkce „Surplus control", která (a) musí být zapnutá v installer menu a (b) řídí podle měřené network balance přes CEM elektroměr. Ani jedno není — projekt CEM C20 + e-Manager vynechal a patrony jdou přes HA relé (viz tepelne_cerpadlo.md). Registry jsou tedy zapisovatelné, ale inertní.

Rozhodnutí / cesty dál

  • v1 = patrony EP1/EP2 přes HA relé (switch.ftv_do_boileru 3 kW, switch.ftv_do_akumulacky 5 kW) — fungují dnes, COP 1, bez TČ commissioningu. Curtailment lze jímat hned, nezávisle na kompresoru.
  • Kompresor (efektivní nadstavba, COP 3–4) má 3 cesty:
  • A-CEM (Veskom zapne e-Manager + doinstaluje CEM): zamítnuto jako default — autonomní honění bilance sítě by se pralo s Victron ESS (oba řídí grid balance).
  • A-BUS (Veskom zapne e-Manager, krmený z HA přes 2777): elegantní, TČ samo moduluje + distribuuje; čeká na Veskom + potvrzení, že BUS-only jede bez CEM.
  • B — HA řídí setpointy přímo (2705 demand + 2720/2722 setpoint po BUS, bez e-Manageru): funguje hned, sedí na AppDaemon architekturu; HA nese bezpečnost (watchdog musí stáhnout setpoint, jinak TČ dotápí ze sítě — downside ale ohraničený: nádrže cappnou, 60 °C cutoff + směšovač). Preferovaná cesta k otestování.
  • Offsety rozhodnuty (pro A-BUS i případně B): TV +12 → 60 °C (HTR, směšovač chrání kohoutky, legionella), AK +18 → 48 °C (standardní kondenzátor — nad ~50 padá COP, AK krmí nízkoteplotní okruhy), bazén odložen (největší sink ~45 kWh/°C, ale gated DI4 + Aseko dual-writer; filtrace běží ~12 h/den + lze spustit přes relé → reálné ve Fázi 2).

Lekce (durable) — single Modbus master

Rollback test-skriptu selhal kvůli kolizi druhého mastera (ModbusIOException — pymodbus souběžně s HA pollingem). Potvrzeno empiricky, ne jen z doku: všechny zápisy do TČ musí jít přes HA modbus.write_register (jediný legitimní master), nikdy přes pymodbus. Read-only probe (scripts/ecoforest_surplus_probe.py) je OK pro diagnostiku (retry), ale zápisy ne. (Kompresor během testu neběžel → bez škody; registry ručně srovnány na 0.)

Operační dopad

Bez runtime dopadu — TČ na nativní regulaci, všechny surplus registry 0. Aktualizován rizeni_energie.md + todos.md. Kompresorová Fáze 2 čeká na rozhodnutí cesty (B test přes HA / dotaz Veskomu na A-BUS).


2026-06-21 — FVE baterka: detekce „plného nabití" přes SOC (vebus float/storage v ESS nefunguje)

Zjištění

sensor.baterka_posledni_plne_nabiti (template.yaml) detekoval plné nabití přes vebus_inverter_state == float/storage. V tomhle AC-coupled + DC-MPPT ESS Multiplus do float/storage NIKDY nepřejde — baterii dobíjejí MPPT + Fronius, top-of-charge řídí DVCC/ESS, Multiplus zůstává v absorption/sustain. Ověřeno: za 48 h jen bulk/absorption/sustain/inverting, žádný float. Značka tak zamrzla na 27. 5. → senzor hlásil falešných „25 dní od plného nabití", ačkoli SOC reálně držel 99,7–99,8 % hodiny každý slunný den (např. 20. 6.). Odhaleno uživatelem (VRM ukazoval baterku včera plnou hodiny).

Rozhodnutí

Detekce přepnuta na SOC ≥ 99 % držené 15 min (numeric_state trigger + HA-start fallback s condition). SOC je v tomhle systému spolehlivý signál „plně nabito"; práh laditelný.

Dopad

Oprava NENÍ kosmetika: plánovaný balancing interlock řízení přebytků (Fáze 1b — dní_od_plného > 14 → potlačit jímání) by s rozbitým senzorem napořád blokoval jímání přebytku FTV. Po nasazení se dní_od_plného srovná, jakmile SOC dnes překročí 99 % na 15 min (HA-start fallback NEsepne na pouhý reload templatu — až na příští plný start / dosažení prahu).

Gotcha (durable): v AC-coupled Victron ESS se nedá spoléhat na vebus_inverter_state float/storage jako na „baterka plná" — používat SOC nebo systémový battery state. Stejná past hrozí u jakékoli logiky vázané na charge-fázi Multiplusu.


2026-06-21 — Závlaha: forecast-only brána nahrazena Open-Meteo vodní bilancí

Rozhodnutí

Dešťová brána binary_sensor.zavlaha_predpoved_deste (forecast met.no, dopředu, denní granularita) nahrazena vodní bilancí krmenou z Open-Meteo (free API, bez klíče):

  • 2 bilance v mm (input_number.zavlaha_bilance_{plocha,sklenik}): denně ve 4:00 bilance += déšť_včera − ET0_včera×kc, clamp ⟨−50,+20⟩. Záměrně včerejší naměřené hodnoty (ne dnešní forecast — odpolední déšť by minul); forecast je jen veto v ranní bráně (skip když dnes/zítra ≥ 5 mm). Plocha počítá déšť, skleník (krytý) jen výpar. Záporná = zalévej, kladná = skip; po zálivce reset na 0. Vydatný déšť → strop +20 → přirozený vícedenní skip. Zdroj: sensor.zavlaha_{et0_dnes,et0_vcera,srazky_dnes,_vcera,_zitra} (REST, rest.yaml).
  • Open-ground jen ránozavlaha_vecer (21:00) vypnuta (zakomentována). Trávník/kapky/stromy večer = houby. Jediný večerní běh zůstal skleník (kapka, bezpečné), navázaný na ET0 místo holé teploty.
  • Stromy oddělené od záhonů (input_number.zavlaha_doba_stromy) — hlubší/řidší dávka.
  • Dávky zmrazeny na osvědčené ~10 min jako runtime tuning, ne návrhové číslo.

Důvod

Incident 20.6.2026: ~2 h po večerní bouřce se ve 21:00 spustila závlaha do kaluží. Z historie: brána byla OFF přesně v okně 20:00–01:00 — denní agregát met.no konvektivní bouřku nezachytil a po přechodu „už neprší" se otevřela. Forecast-only brána je principiálně slepá ke spadlému dešti. Open-Meteo dává naměřené srážky (past_days) + korektní ET0 (FAO-56) zdarma → bilance řeší přesně tu třídu selhání.

Zamítnuté alternativy (7 iterací ideace):

  • Smart Irrigation (HACS) — instalovaný, ale rozbitý (bucket −362 mm, reset_bucket se nevolá, orphaned automatizace unavailable) a config-divadlo (throughput/soil/drainage). V gate režimu nepřináší nic navíc; ruční bilance je transparentní a bez závislosti.
  • Deep-infrequent — půda nedovolí: ~40 cm zeminy/navážky nad nepropustným jílem → mělčí kořenová zóna, perchování, sklon k podmáčení (empiricky 10 min v chladnu podmáčelo stínná místa). Primární riziko = přelití. Navíc celá výsadba teprve zakořeňuje. „Hluboko a zřídka" je budoucí knob (zvednout práh), ne letošek.
  • Rotace okruhů / 3+ kýble — malé osvědčené dávky dělají rána krátká, špička zmizí i bez rotace; frekvenci řeší ET, víc kýblů zbytečné.

Operační dopad

Nové entity: 4 REST sensory (rest.yaml), 3 helpery (input_number.yaml), automatizace zavlaha_vyhodnoceni_bilance (04:00). Upraveny zavlaha_rano (brána = bilance < −5 a forecast < 5 mm), zavlaha_zahony_{rano,vecer} (reset bilance, večer dle ET0), okruh 7 → doba_stromy. binary_sensor.zavlaha_predpoved_deste ponechán jako informativní. Dokumentace: zavlaha.md. Ruční úklid (mimo repo, .storage): smazat dormant automation.sensor_smart_irrigation_travnik_2. Bez restartu — stačí reload. Ladění prahů/dávek probíhá za provozu (první 1–2 týdny).


2026-06-20 — Řízení přebytků FTV: revize návrhu po multiagentním review

Rozhodnutí

Návrhová rozvaha rizeni_energie.md zpřesněna po adversariálním review. Změny oproti původní rozvaze (2026-06-17):

  • v1 = nativní HA YAML, ne AppDaemon. Kompresor (priorita #1) NEMÁ v HA zapisovatelnou entitu (Ecoforest read-only), takže buildovatelná v1 = patrony (EP2 5 kW → EP1 3 kW) + léto klima soak (~60–70 % hodnoty). Na dvě relé + script netřeba watchdogovaný AppDaemon mozek; ten je až Fáze 2 pro spojitou kompresorovou modulaci.
  • Fáze 0 verification gate. Před kódem ručně ověřit zapisovatelnost Modbus 2775/2777. Surplus registry jsou čitelné (ověřeno read-only scripts/ecoforest_surplus_probe.py, hodnoty 0), zapisovatelnost neověřena (vyžaduje „Bus control" v menu TČ + write test dle procedury v hlavičce skriptu).
  • Signál curtailmentu dvoustupňový. MPPT voltage_current_limited sám je falešně pozitivní (fíruje při CC/CV dobíjení plné baterie) → přidána PV-rise potvrzovací smyčka.
  • Práh diverze = OTE 0,50 (HW realita firmy), ne 0,30; v pásmu 0,30–0,50 firma neexportuje → jímat doma (rozhodnutí uživatele).
  • Balancing interlock: dokud dní_od_plného > 14 A SOC < 100, surplus diverze potlačena (nech baterii dojít na 100 % pro BMS balancing). Live 23,5 dne = po termínu.
  • ESS firmy nedotýkat — řídit jen loady (žádný zápis do ac_power_set_point ani dess_mode).

Důvod

Review (3 čočky: KISS / safety / grounding) ukázalo, že původní 9-stupňový AppDaemon návrh byl předimenzovaný vůči tomu, co jde dnes postavit, a obsahoval chyby: vymyšlené entity (ecoforest_aux_*), rozbité čtení spot ceny (atribut now() → None), falešně pozitivní signál, chybný práh 0,30 místo firemních 0,50, chybějící balancing interlock a fail-safe nezávislý na mozku. KISS: v1 řeší curtailment dvěma relé + scriptem v nativním YAML; AppDaemon až tam, kde má spojité řízení smysl (kompresor).

Operační dopad

Aktualizován rizeni_energie.md (signál, architektura v1/Fáze 2, fázovaný rollout). Nový read-only skript scripts/ecoforest_surplus_probe.py + ruční write-test procedura (Fáze 0). Bez runtime dopadu — žádné řízení zatím neběží. Side-finding z probe (2026-06-20): TČ hlásí alarm level 4 (long-time) a stojí idle s DHW 43,5 °C pod setpointem 48 °C — ke kontrole (aktivní alarm kód v registru 2069).


2026-06-20 — FVE: doložená geometrie polí, mapování stringů a empirický curtailment

Zjištění (z projektové dokumentace + živé měření 2026-06-19)

Do repa zařazena kompletní projektová dokumentace FVE (docs/manualy/fve_marada: technická zpráva, jednopólové schéma, výchozí revize). Z ní + z měření v HA plyne:

  • Geometrie polí (dosud chybějící vstup pro Forecast.Solar): 58 panelů AIKO NEOSTAR 2S A450 (450 Wp) na dvou střechách:
  • Dům: 32 ks, azimut 121° (JV), sklon ~35°.
  • Carport: 26 ks, sklon ~2° (téměř plochý), dělení ≈ 1/3–2/5 na jih, zbytek na sever — při 2° je rozdíl J/S skoro kosmetický.
  • Mapování stringů na střechy a měniče (dovozeno z počtů panelů 32 + 26 a typu zapojení; schéma to explicitně neuvádí): dům = stringy 17s1p + 15s1p (688/607 V) → Fronius Symo; carport = 5s2p + 2× 3s2p + 2s2p (202/120/120/81 V) → 3× Victron MPPT. Sedí na počty i na to, že dlouhé sériové stringy patří na jednu koplanární rovinu (Fronius), nízkonapěťové paralelní na členitý plochý carport (MPPT).
  • Proč baterie padá na 100 % k polednu: JV orientace hlavního pole (35°) posouvá špičku výroby do dopoledne. S 64 kWh baterií a letním nízkým odběrem se úložiště naplní kolem poledne → curtailment brzy odpoledne, ne v pravé poledne. Empiricky 2026-06-19: poledne plný výkon (~14 kW), krátké propady výroby na ~0,5 kW ve ~13:47 a ~14:42 (nejlevnější čtvrthodiny spotu, baterie 100 %).

Rozhodnutí

  • Cenový práh dodávky firmy (OTE < 0,5 → nedodávat) se nechává být. Skutečný break-even výkupu je OTE 0,30 (výkup = OTE − 0,30); firma dala 0,5 jako konzervativní buffer. Rozdíl je finančně marginální (řád desítek Kč/rok — pásmo 0,30–0,50 má výkupní marži 0–0,20 Kč/kWh na pár čtvrthodin denně). Hodnota není v ladění prahu, ale v jímání mařeného přebytku do tepla (viz rizeni_energie.md).
  • Curtailment Victronu je interní (frekvenční posun na Fronius + škrcení MPPT) — HA do něj nezasahuje. Řízení přebytků proto cílí na přidávání domácích spotřebičů (TČ kompresor → patrony → wallbox → chlazení), ne na boj s ESS firmy.

Operační dopad

Dokumentace: aktualizován ftv.md (orientace polí, mapování stringů, vstupy pro Forecast.Solar, firmou nastavený práh 0,5), rizeni_energie.md (odblokován krok Forecast.Solar, empirické chování), todos.md. Bez runtime dopadu. Odblokovává Forecast.Solar (geometrie známá) → krok k prediktivní ranní arbitráži.


2026-06-19 — HA default route přes IoT NIC: trvalá oprava odebráním gateway z enp0s19

Symptom

Otevření branky přes HA (PUT http://192.168.60.10/ISAPI/AccessControl/RemoteControl/door/1) padalo na timeout. sensor.doorbell_call_status byl unavailable od 11:23. Zvonek přitom z Macu (VLAN 20) odpovídal okamžitě (401 za ~5 ms, ISAPI zdravé) a na síti byl živý (ARP, DNS pokusy).

Root cause (živě ověřeno)

HA host má dvě NIC, obě měly nastavenou gateway (enp0s18→20.1, enp0s19→40.1). HAOS si proto sám volí primární a v 11:23 ji přehodil na enp0s19 (VLAN 40 / IoT). Tím šel default route přes IoT VLAN → pfSense pravidlo „Block IoT to Internal Networks" (interface IOT/opt2, log: falsetichý drop) zařízlo HA→VLAN 60. Same-subnet provoz (Fronius 40.25 na VLAN 40, Scrypted 20.21 na VLAN 20) jel dál → maskovalo to příčinu a svádělo na zařízení. Recidiva incidentu 2026-05-06 (HA OS upgrade přehodí primary NIC).

Mylné mezizávěry během debugu (zaznamenané jako varování): „zaseklé ISAPI zvonku", „session leak z 1s pollu", reboot zvonku. Žádný neplatil — zvonek byl celou dobu zdravý.

Rozhodnutí

Default route se zafixuje na VLAN 20 odebráním gateway z enp0s19 (IoT NIC gateway nepotřebuje, jen same-subnet L2). Když má gateway jen enp0s18, výběr default route je deterministický a HAOS ho nemůže přehodit.

ssh root@192.168.20.7 'ha network update enp0s19 --ipv4-method static --ipv4-address 192.168.40.7/24 --ipv4-gateway "" --ipv4-nameserver ""'

Důvod / klíčové zjištění

  • UI gateway smazat neumí (validace IoT NIC to nedovolí) — proto dřívější pokusy uživatele „nepřežily". CLI ha network update --ipv4-gateway "" tu validaci obejde a drží.
  • Ověřeno: po ha network reload zůstala gateway smazaná, primary na enp0s18, HA→zvonek 401, entity naskočily na idle. Reload re-aplikuje z perzistované konfigurace = silný proxy perzistence.
  • Lepší než plánované route-metric pinning (todo z 2026-05-06): bez gateway nemůže být enp0s19 default route vůbec, nezávisle na primary flagu i metrice.
  • 1s poll zvonku s incidentem nesouvisel — neměnit kvůli tomuhle.

Operační dopad

HA→VLAN 30/50/60 obnoveno (branka, kamery přímo, MEDIA). Změna je na HA hostiteli, ne v repu (mimo ha-deploy). Aktualizováno homeassistant.md (Dual-NIC), memory ha-dual-nic. Follow-up: ověřit perzistenci po nejbližším plném rebootu / HA OS upgrade (viz todos.md) — pokud se gateway vrátí, fallback je úzké pfSense pravidlo 192.168.40.7 → 192.168.60.10.

2026-06-20 — AI Log Monitor: triáž bez šumu, re-surface prahem, vlastní logging

Kontext

Otevřené GitHub issues od ai-monitor bota se hromadily (21 otevřených, většina historická nebo benigní). Živá kontrola sítě ukázala, že část byla stale (#52 — AP dávno online), neexistující/přejmenované zařízení (#73 — „U7-Pro-Outdoor" = AP8 carport, benigní wevent Resource busy šum), nebo boot-only událost re-flagovaná denně (#43 — systemd-networkd-wait-online). Monitor navíc neměl jak issues zavírat a re-komentoval totéž každých 12 h.

Rozhodnutí

Ladění scripts/log_monitor.py:

  • Auto-close zamítnut — issues zavírá člověk ručně. Monitor je sám nezavírá (riziko zavření reálného problému během slepého běhu, kdy mlčí Loki/zdroj).
  • Re-surface jen při seriózním opakování: otevřené issue se znovu nekomentuje každý běh; re-comment + Telegram jen P0 vždy, P1 až při occurrence_count ≥ SERIOUS_RECURRENCE_THRESHOLD (default 50 / 12 h). Přidán occurrence_count do Gemini JSON.
  • Tvrdší triáž v promptu: přechodné jednorázovky (Tailscale „context canceled"), boot-only selhání na hostu s dlouhým uptime a známý benigní šum → P2 (nezakládá issue).
  • Vlastní strukturované logování (logging): INFO→stdout, WARNING/ERROR→stderr→journald→Loki. Chyby samotného monitoru (Loki/Gemini/GitHub/Telegram) jsou dohledatelné i v logu, ne jen v Telegramu; při nevalidním JSONu z Gemini se loguje prvních 800 znaků odpovědi.

Důvod

  • Žádné count-gating zakládání nových issues: reálná kritická chyba (degradovaný ZFS pool, SMART fail, OOM, ztráta kvóra) se v okně objeví třeba jednou — gating podle počtu by ji potlačil. Suprese šumu je sémantická práce Gemini (priorita), ne mechanický práh. Práh se používá jen pro re-surface už založeného problému.
  • Self-monitoring gap: Gemini Flash občas vrátí nevalidní JSON (responseMimeType: application/json to nezaručuje). Předtím to šlo jen do Telegramu; teď i do logu se vzorkem odpovědi → debugovatelné.

Operační dopad

  • Žádný HA runtime dopad. Skript běží na debian-services (192.168.20.20) přes systemd timer 08:00/20:00; deploy ruční scp do /opt/scripts/ (service/timer beze změny).
  • Práh 50 je prvotní odhad — doladit podle reálného provozu.
  • Dokumentace: log_monitor.md.
  • Související tooling gotcha (mimo tento soubor, v paměti agenta): pfSense MCP hlásí balíček Tailscale jako stopped/disabled i když běží — ověřovat v GUI, ne přes MCP service status.

2026-06-18 — Zahradní domek: fasáda modřínové plotovky 100/121/146, bez nátěru, rohy a větrání

Rozhodnutí

Vnější svislý rastr se mění z řezaného KVH 120×19 (60/80/100/120 mm) na kupované modřínové plotovky tří frézovaných šířek 100 / 121 / 146 mm (~21 mm), mezera ~13 mm. Modřín bez nátěru — jednorázový bezbarvý Bochemit před montáží, pak přirozené šednutí a žádná údržba. Finální sekvence prken, rozměry runs a detaily v zahradni_domek.md.

Důvod

  • Tři hotové šířky místo řezání: odpadá podélné řezání na stolní pile i 15% rezerva na odřezky; konzistentní frézované hrany. Spread 100 : 121 : 146 drží výrazný nepravidelný rytmus.
  • Bez nátěru = záměr i KISS: přirozené šednutí modřínu je esteticky preferované; modřín (trvanlivost tř. 3–4) se uživí sám. Bezbarvý Bochemit (NE pigmentovaný Estetik, který by bránil šednutí) jako jediná, jednorázová ochrana — po montáži jsou záda v otevřeném rastru nepřístupná. Tím padá Rubio DuroGrit i údržba à 3–5 let.

Klíčové konstrukční závěry

  • Rozměry obkladu (run) ≠ holé OSB. Latě stojí 40 mm nad OSB → každá stěna přesahuje. Hierarchie překryvu v rozích přední > boční > zadní: přední run = OSB +122 mm, boční +101 mm, zadní +80 mm. Finální runs 3112 / 3075 / 2081 mm.
  • Větrání — korekce dřívějšího omylu: vodorovné latě přerušují svislý tah za rastrem, takže žádný „komín sání dole / výdech nahoře". Otevřený rastr (~10 %) dýchá celou plochou napřímo. Reálné role: folie (pojistná rovina) + sražená hrana latí 15° + segmentované latě (mezírka pro odvod vody). Velká větrací štěrbina pod podhledem proto není potřeba.
  • Roh střecha–stěna — rohová lať drží folii: v koutě se dává rohová lať (KVH 60×40, sražená hrana), přišroubovaná skrz přeložený spoj folií (střešní shingle přes stěnovou) do věnce → mechanicky sevře lap po celé délce. Plotovky běží v plné výšce až ~5–10 mm pod podhled (jen dilatace), žádná 15–20mm štěrbina. (Reviduje dřívější závěr „folii drží jen věnec + podhledové plotovky, ne stěnová lať" — komín neexistuje, takže zavřít horní hranu latí je v pořádku; podmínka: shingle zůstává, střešní folie lapuje PŘES stěnovou, roh. lať je vně folie.)
  • Vruty: jeden v ose na lať — i u 146mm. Dva vruty s vodorovným rozestupem (vedle sebe i diagonálně) → prkno praskne přes šířku. Diagonála problém neřeší. Miskovatění 21mm plotovky se bere jako součást přírodního vzhledu.
  • Dělení 146 napůl zamítnuto: práce navíc, zabíjí akcent rytmu, syrový pilový řez nesedí k frézovaným hranám.

Operační dopad

Žádný runtime dopad (mimo HA). Aktualizován zahradni_domek.md: materiál, skladba, sekvence kladení, rohy, rozmístění latí, vruty, ochrana dřeva, materiálový odhad, postup i fáze realizace.


2026-06-17 — Řízení přebytků FTV: architektura AppDaemon + návrhová rozvaha

Rozhodnutí

Řízení přebytků FTV (jímání přes TČ surplus + spotová arbitráž dodávky) se postaví jako AppDaemon appka podle vzoru existující appdaemon/apps/daikin_modbus.py — ne jako HA automatizace ani samostatná služba. Charakterizační měření jako scripts/ Python skript. Celá návrhová rozvaha (kaskáda, principy, defenzivní arbitráž, balancing, nepravidelné spotřebiče) je v rizeni_energie.md. Zatím neimplementováno — řízení neběží.

Důvod

  • AppDaemon už je zavedený a používaný (daikin_modbus.py řídí Daikin přes Modbus, foxtron_scene_controller tlačítka). Není to nová moving part — tím padl hlavní KISS argument proti. Naopak daikin_modbus.py je hotový vzor přesně pro tenhle úkol: zápis přes modbus.write_register, periodic guard = watchdog, debounce, HA služby. (Tím se fakticky uzavírá i todo „AppDaemon zachovat/eliminovat" → zachovat.)
  • HA automatizace odmítnuty pro mozek: predikční matematika (forecast × absorpce × spot × SOC) a watchdog v Jinja jsou křehké a nečitelné. Python je uživatelova silná stránka.
  • Human-operability zajištěna i s Pythonem: kill-switch input_boolean, stav publikovaný do HA senzorů, watchdog vrací TČ vlastní regulaci. Dům funguje i bez appky.

Klíčové designové závěry (detail v rizeni_energie.md)

  • Self-consumption ≫ dodávka (fix 3,98–4,92 vs výkup OTE−0,30) → kaskáda kompresor → baterka → patrony → wallbox/chlazení → dodávka → curtailment.
  • Proaktivní TČ (pustit do tepla brzy, ne čekat na plnou baterku — kompresor 6 kW je úzké hrdlo vs 26 kWp).
  • Přetok 12,5 kW = vzácný zdroj → alokovat do drahých hodin; baterka = časový posun z levného poledne.
  • Defenzivní ranní arbitráž vázaná na pesimistický dvoudenní forecast + tvrdý SOC floor.
  • Full charge ≥ 1×/14 dní (BMS balancing) má přednost před arbitráží, načasovat na slunný den.

Operační dopad

Žádný okamžitý — designové rozhodnutí pro budoucí implementaci. Postup po krocích (zapisovatelnost → charakterizace → appka) v todos.md a session plánu.


2026-06-16 — Atrea rekuperace: Modbus mapa, opravené chyby a model řízení

Zjištění (živě ověřeno sondou na 192.168.40.33)

Atrea aMotion má celou čtecí mapu jako input registry (FC04) — teploty, průtoky, stavy, dokonce i „config/type" registry 1201–1206. Holding registry (FC03) existují jen pro zapisovatelný „request" blok kolem 1001–10xx (work_regime 1001, temp_request 1002, fan_power_req 1004, …). Čtení holdingu mimo tento blok vrací exception 2 (Illegal Data Address).

Rozhodnutí

  • Root cause „chronicky nemocné atrea": 5 senzorů (1201, 1202, 1203, 1205, 1206) bylo chybně input_type: holding → exception 2 každých 5 min, zaplavovalo error log a dělalo z reload_all minu (doorbell incident 2026-06-14). Opraveno přepnutím na input_type: input. Tím je root cause pryč; opatrnost vůči reload_all v ha-deploy skillu ale zůstává jako obecná zásada.
  • Model řízení = jen výkon ventilátorů zapisovat, zbytek číst. Aktivní zápis pouze do fan_power_req (holding 1004) přes input_number.atrea_vykon_ventilatoru + automatizaci atrea_zapis_vykonu_ventilatoru (mode: restart kvůli limitu 30 relací / 30 s). Režim (work_regime) a bypass zůstávají read-only.
  • Bypass se neřídí ručně. Bypass v auto módu je samostatná termostatická automatika nad teplotami (otevře, když Ti > Tp a zároveň Te < Ti). Ruční zápis by bojoval s protimrazovou/komfortní logikou jednotky. Free-cooling bypass je ale podmíněný netopnou sezónou — v topné sezóně jednotka prioritizuje udržení tepla a bypass pro chlazení nepovolí (jen ochranné pohyby).
  • Sezóna se řídí přes H1010, práh na Modbusu není. Jednotka byla v topné sezóně i v červnu (I1010=2), protože H1010=0 (auto dle průměrné T-ODA) a servisní práh ~16 °C je na dobře izolovaný dům s velkými solárními zisky moc konzervativní. Nastaveno H1010=1 (auto dle průměrné T-ODA + tepelný zisk) → jednotka překlopí do netopné sezóny dřív, když detekuje vnitřní zisky. Práh teploty je servisní parametr v aTool, na Modbusu nedostupný (jde měnit jen režim přepínání). HA ovládání: input_select.atrea_rezim_sezony + automatizace atrea_zapis_rezimu_sezony; čtení: sensor.rekuperace_sezona (I1010), sensor.atrea_season_setting (H1010).
  • Noční chlazení (work_regime 5) je vrstva nad bypass automatikou — když jsou splněné podmínky, navýší průtok ventilátorů pro rychlejší noční výplach tepla. Není to proxy na bypass. Proto se trvale nezapíná (přes den nic navíc nepřinese, jen vyšší spotřebu/hluk); spíná se cíleně z HA jen pro agresivní noční předchlazení.
  • Autoritativní register mapa je nově v repu: docs/manualy/atrea_duplex/ (oficiální Atrea modbus_comm aMotion v04). Jednotka používá aMotion 4místné adresování (H10xx/I10xx), ne RD5 5místné.

Důvod

KISS a bezpečnost: výkon ventilátorů je jediná osa, kterou má smysl aktivně řídit (jednotka jede v power módu, fan_control_type=0). Průtokové registry reálně vrací 0 (pravděpodobně bez osazeného průtokového/tlakového čidla) — řídit přes flow_*_req by bylo k ničemu. Bypass i noční chlazení si jednotka obstará sama lépe, než by to dělala domácí automatizace.


2026-06-16 — Spotové ceny (OTE) + HDO tarif (NT/VT) jako vstup pro budoucí řízení dodávky FTV

Rozhodnutí

Nainstalovány dvě HACS integrace: rnovacek/homeassistant_cz_energy_spot_prices (spotová cena OTE, 15min, Kč/kWh) a Cmajda/ha_cez_distribuce (HDO NT/VT podle EAN odběru). Zatím jen sběr dat — žádné řízení dodávky/spotřeby. Detaily entit a obsluha: ceny_a_tarify.md.

Důvod

  • Výkup za spot, odběr za fix — od 13. 6. 2026 připojena FTV výrobna. Výkup u EnerSpot = OTE spot − 300 Kč/MWh (0,30 Kč/kWh), odběr fix (E.ON Variant). Spotová cena je relevantní jen pro rozhodnutí „dodávat / nedodávat přebytek".
  • rnovacek zvolen místo ha_epex_spot / Ge Spot — nativní OTE pro ČR, přepočet ČNB, 15min granularita sedící s EnerSpot zúčtováním; KISS, bez API klíčů.
  • Korekce prahu — práh není 0, ale 0,30 Kč/kWh. Původní záměr „nedodávat při záporném spotu" je moc nízký: v pásmu 0–0,30 Kč/kWh je OTE kladný, ale výkup po odečtu marže záporný → dodávka prodělečná. Práh pro dodávku = OTE ≥ 0,30 Kč/kWh.
  • HDO (D57d) přidáno, ale nesouvisí přímo se spot výkupem — řídí odběr (kdy TČ topí za NT), užitečné až pro self-consumption (přebytek do TČ/bojleru, když se dodávka nevyplatí).

Operační dopad

  • Obě integrace žijí jako HACS custom_components + config entry v .storage, tj. mimo git (gitignored). Po reinstalaci HA nutná ruční reinstalace přes HACS + config flow — viz „Co dělat, když to nefunguje" v ceny_a_tarify.md.
  • Samotné řízení dodávky (template senzor výkupní ceny, binary „vyplatí se dodávat", napojení na střídač/curtailment) je otevřená položka — viz todos.md.

2026-06-15 — Syslog ingest přesunut z HA na standalone Alloy na debian-services (logging review ⑧)

Rozhodnutí

Syslog collector už neběží v HA Alloy addonu. Nový standalone Alloy (grafana/alloy:v1.16.3, docker) na debian-services přijímá veškerý syslog (UDP 5514 / TCP 5601) a píše do lokálního Loki. HA Alloy addon osekán na jen journal pipeline. Relay (nativní rsyslog na debian-services) zůstává a jen přesměrován na lokální Alloy.

Důvod

  • Odstranění SPOF: dřív Alloy syslog listener běžel na HA → restart/výpadek HA (časté, kvůli config deployům) zahodil syslog ze VŠECH zařízení právě v okamžiku incidentu (UDP se nebufferuje). Teď syslog ingest sedí u Loki na debian-services — výpadek HA logování zbytku domu neovlivní.
  • Konec round-tripu: zdroje dřív tekly debian (relay) → HA (Alloy) → debian (Loki). Teď vše lokální na debian-services.
  • Journal zůstává na HA záměrně: systemd journal HA Core/addonů se čte lokálně, nelze sbírat vzdáleně. Dva collectory podle lokality = správně, ne duplikace. Sjednocení na jeden (forward journalu jako syslog) zamítnuto — ztratilo by strukturovaná pole (unit/container_name) a rozbilo HA dashboardy.

Provedení / verifikace

Bezpečné pořadí bez výpadku ingestu: ① nový Alloy additivně (test RFC5424 → hostname OK) → ② přepnout relay forward 192.168.20.7:5514127.0.0.1:5514 → ③ repoint pmx/PBS 99-loki.conf na 192.168.20.20:5514 → ④ oseknout HA addon (deploy přes ha-deploy, restart addonu). Po každém kroku ověřeno v Loki přes servername label (debian-services vs HomeAssistant). Výsledek: 100 % job=syslogservername=debian-services, journal teče dál, oba freshness alerty normal.

Operační dopad

  • Nový kontejner alloy v docs/files/docker/debian-services/docker-compose.yml; config docs/files/docker/debian-services/alloy/config.alloy. Deploy přes scp + docker compose up -d alloy.
  • Relay config nově verzován: docs/files/config_backups/debian-services-relay/.
  • Nový přímý RFC5424 zdroj → docs/files/config_backups/99-loki.conf (cílí na 192.168.20.20:5514).
  • Bracket [hostname] hack i relay zatím zůstávají — standardní Alloy build by mohl __syslog_message_hostname populovat nativně (retire bracketu + relay), ale neověřeno; ponecháno jako budoucí optimalizace.

2026-06-15 — Zrušen automatický GitOps deploy → ruční deploy přes skill ha-deploy

Rozhodnutí

Odstraněn celý HA-side auto-sync: automatizace System - Git Auto-Sync (5min cron), HA GitOps - Apply Synced Changes, HA GitOps - Failure Alert; skripty auto_sync.sh, safe_update.sh, preview_update.sh; status/lock soubory a sensory ha_safe_update_*. Nasazení teď řídí skill ha-deploy (.claude/skills/ha-deploy/): po git push na master udělá pull na HA (shell_command.ha_git_deploy = git reset --hard origin/master), config check (s rollbackem při chybě) a cílený reload nebo restart podle změněných souborů.

Důvod

  • Autonomie řešila neexistující problém. Každá změna stejně prochází pracovní session (commit/push dělá asistent na pokyn) — HA si nikdy nemusí změnu „najít sám". Auto-sync byl overengineering pro tenhle workflow.
  • Byl aktivním zdrojem incidentů. Za dva dny dvakrát: lock/status race (deploy se neaplikoval, viz entry 2026-06-14) a reload_all zaseknutý na nemocném modbus atrea (2 h výpadek rest+template domény, viz entry 2026-06-14). Méně pohyblivých částí = ména ploch na selhání.
  • Cílený reload místo reload_all. Skill volá jen automation.reload / template.reload / rest.reload … podle toho, co se změnilo — reload_all (který kaskáduje přes modbus a zasekl se) se už nepoužije.

Operační dopad

  • Po push se deploy nestane sám — musí ho spustit skill ha-deploy (asistent) nebo člověk ručně přes Vývojářské nástroje → Akce → shell_command.ha_git_deploy + reload/restart v UI. Postup bez AI je zdokumentován v homeassistant.md.
  • Ztracená drift-protection (auto reset --hard při lokální editaci) je nahrazena disciplínou „edituj jen v gitu" + pasivním binary_sensor.ha_gitops_stale (bez alertu).
  • Zachována pasivní viditelnost: sensor.ha_config_commit vs. sensor.ha_github_commit, binary_sensor.ha_gitops_stale.

2026-06-14 — Zvonek unavailable: GitOps reload_all zaseknutý na modbus atrea (ne digest, ne breaking change)

Příčina

Po ranním HA update-restartu na core-2026.6.3 se v 06:49 UTC spustila automatizace „HA GitOps - Apply Synced Changes" a zavolala reload_all. Ten reload se zasekl na trvale nezdravém modbus atrea (registry 1201–1206 vrací pymodbus isError) a nedoreinicializoval rest ani template doménu. Setup domén při startu byl přitom úspěšný a rychlý (rest 0.20 s, template 0.02 s) — entity fungovaly ~10 min (anyone_home naběhl on v 06:40), než je ten reload_all shodil. Jde o druhý projev GitOps apply problému (viz entry „GitOps apply: race mezi lock a status sensorem" níže).

Důsledky

Všechny rest + template + modbus entity zůstaly unavailable ~2 h: binary_sensor.doorbell_ring, sensor.doorbell_call_status, binary_sensor.anyone_home, FTV/Victron/Ecoforest template sensory atd. Prakticky: nešlo zvonění (automatizace zvonek_zvoneni_a_notifikace nikdy nedostala trigger), zmizely odvozené FTV/klima senzory.

Oprava

Ruční template.reload (oživil template doménu bez výpadku) → čistý homeassistant.restart (obešel zaseknutý reload_all, všechny domény naběhly zdravě) → shell_command.reset_update_status (status visel na restart). Čistý restart degradaci nezpůsobuje — problém je výhradně reload_all, ne start.

Falešné stopy (zdokumentováno, ať se nehledá znovu)

  • httpx digest (#85888): vyvráceno — httpx 0.28.1 (verze v core-2026.6.3) DigestAuth proti doorbell ISAPI callStatus ověřeně vrací HTTP 200/idle. Digest funguje.
  • Breaking change „legacy template syntax" (2026.6): netýká se — config nemá platform: template nikde (jen modbus sensors:, jiná integrace). template.yaml je moderní template: syntax.

Rozhodnutí — doorbell zůstává na nativním REST

Ring detekce zůstává na rest sensoru (rest.yaml, digest přes httpx). Dočasně nasazený command_line+curl --digest workaround (commit 81e1fa6) byl vrácen (ab5e691), protože digest funguje a REST je KISS jednodušší — bez shell skriptu a parsování hesla ze secrets.yaml. Entity registry pak vyžadovalo ruční úklid (smazat orphaned *_cmdline záznamy, přejmenovat REST *_2 → původní entity_id).

Lessons / follow-up

  • reload_all je jen tak robustní jako nejnemocnější YAML integrace. Nemocný modbus atrea udělá z reload_all minu. Viz todos: guard/oprava atrea + zvážit nepoužívat automatický reload_all po startu.
  • Diagnostická past: silná korelace „rozbilo se to po updatu" svedla na unáhlenou úzkou hypotézu (digest). Skutečný rozsah (celá rest+template doména, ne jen doorbell) ukázal jiný root cause. Loki HA core logy ze startu ({service_name="homeassistant"}) byly klíčové — Setup of domain … časy + Apply Synced Changes v 06:49.

2026-06-14 — Hostname labely pro infra syslog (pmx/PBS template, pfSense přes relay)

Rozhodnutí

Všechny syslog zdroje teď do Loki dorazí s hostname labelem. pmx1/pmx2/PBS: jejich 99-loki.conf template prependuje [%HOSTNAME%] do MSG (forward zůstává přímý na HA:5514). pfSense: přesměrován z přímého HA:5514 na rsyslog relay 192.168.20.20:514.

Důvod

Alloy loki.source.syslog v community buildu (v0.0.8) nepopuluje __syslog_message_hostname; jediný funkční způsob extrakce hostname je regex nad [hostname] prefixem v těle zprávy (loki.process stage). Stockový RFC5424Format template prefix nepřidává → pmx1/pmx2/PBS/pfSense dřív dorazily bez hostname (nešlo je rozlišit). Oprava per-zdroj:

  • pmx/PBS mají vlastní rsyslog → stačilo do jejich self-defined RFC5424Format template doplnit [%HOSTNAME%] (chirurgická změna, přímá cesta zachována, ověřeno proti funkčnímu relay template).
  • pfSense GUI neumí vlastní syslog template → nelze přidat bracket na zdroji, proto přesměrován na relay, který bracket přidává centrálně. Trade-off: pfSense logy závisí na relay (debian-services), ale ten hostuje i Loki — žádný nový SPOF.

Operační dopad

  • LogQL filtrování {job="syslog", hostname="pmx1"} atd. funguje pro celou infrastrukturu; dashboardy „Syslog Overview" / „Infrastructure Health" (agregace per hostname) jsou tím smysluplné.
  • pfSense hostname = pfSense.marada.lan (FQDN, ostatní mají krátké jméno — kosmetická nekonzistence, viz todos ⑦).
  • Nový přímý RFC5424 zdroj nasazuj přes verzovaný docs/files/config_backups/99-loki.conf (obsahuje bracket template), jinak dorazí bez hostname.

2026-06-14 — Grafana log-freshness alerting + patch bump (odložení majoru na 13.x)

Rozhodnutí

Nasazen provisionovaný Grafana alerting (Telegram contact point + policy + dvě „log source silent" pravidla pro journal a syslog), nový dashboard „Log Pipeline Health". Grafana bumpnuta jen v rámci minor větve 12.3.3 → 12.3.7, ne na major 13.0.2.

Důvod

  • Freshness alerting je přímá pojistka proti incidentu z 2026-06-12 (mrtvý journal 3 měsíce bez povšimnutí). noDataState: Alerting chytí i úplné zmizení jobu, ne jen pokles na nulu — přesně ten případ.
  • Patch místo majoru: 13.0.0 je stará ~2 měsíce; major verze Grafany nesou regrese typicky právě v provisioningu/alertingu, který jsme zrovna nasadili — stackovat major na čerstvou věc = zbytečné míchání proměnných při případném debugu. 12.3 je aktivně patchovaná (12.3.7 z 9. 6.), takže žádná urgence. Na 13.x až vyjde 13.1.x (odchycené .0 regrese) nebo až se 12.3 blíží EOL.
  • chatid jako literál, ne env: Grafana při env-expanzi provisioning hodnot zahodí string-quoting u čistě numerické hodnoty → chatid z env selže na unmarshal. Chat id navíc není secret (bez bot tokenu je k ničemu), takže literál v repu je čistší než křehký workaround. Secret (bot token) zůstává v env.

Operační dopad

  • Výpadek log pipeline (journal i syslog) teď přijde na Telegram do ~15 min. Stejný bot/chat jako log_monitor.py.
  • Alerting je provisionovaný = v UI read-only; změny jen přes repo + redeploy Grafany.
  • Provisioning failure je pro Grafanu fatální (nenastartuje) — YAML chyby v provisioning/alerting/ shodí celou Grafanu, ověřuj logy po deployi.

2026-06-14 — GitOps apply: race mezi lock a status sensorem (restart se neaplikoval)

Příčina

Automatizace „HA GitOps - Apply Synced Changes" měla tvrdou podmínku binary_sensor.ha_safe_update_running == off. safe_update.sh ale zapíše status (reload/restart) až úplně na konci a hned exitne — lock adresář mizí přes trap v řádu milisekund po zápisu statusu. HA čte obě hodnoty dvěma nezávislými command_line sensory s pollem 5 s; status sensor zaznamenal změnu na restart dřív, než lock sensor stihl přečíst, že lock už je pryč. Trigger se spustil, podmínka viděla zastaralé on → akce se zahodila, restart se nikdy neprovedl a status visel na restart.

Důsledky

Deploy TČ Energy (2026-06-12) se zasekl — commit stažen, ale HA nerestartovalo, takže nové entity nebyly. Obejito ručně (shell_command.reset_update_status + homeassistant.restart). Postihlo by to každý restart-class deploy.

Oprava

Tvrdá podmínka nahrazena wait_template na binary_sensor.ha_safe_update_running == 'off' (timeout 30 s, continue_on_timeout: true) na začátku akce. Lock je v okamžiku triggeru fakticky vždy už uvolněný (status se nastaví až po dokončení), takže wait jen dopolluje zaostávající sensor a po timeoutu bezpečně pokračuje. automations.yaml je reload-class, fix se nasadil bez výpadku.

Lessons / follow-up

  • Pozor na podmínky čtoucí dva nezávisle pollované sensory, které mají být konzistentní — mezi nimi je vždy okno až do délky poll intervalu. Buď jeden zdroj pravdy, nebo wait_template místo bodové podmínky.

2026-06-14 — mDNS hostname-conflict storm na HA (Avahi reflector × dual-NIC)

Příčina

HA OS má dvě NIC na stejném hostu: enp0s18 (LAN, 192.168.20.7) a enp0s19 (IoT VLAN, 192.168.40.7). systemd-resolved publikoval homeassistant.local na LAN. pfSense Avahi reflector (allow-interfaces=vtnet1,vtnet2,vtnet3,vtnet4) tenhle announcement reflektoval z LAN (vtnet1) do IoT VLAN (vtnet3), kde ho HA druhý NIC enp0s19 zachytil jako „cizí" host claimující vlastní jméno → konflikt → přejmenování homeassistantNN+1 → znovu announce → reflektuje se → nekonečná eskalace.

Důsledky

~10 přejmenování/s, ~37 700 řádků/h balastu do systemd journalu a Loki (job="journal"), čítač published jména dosáhl 30 000+. Systémový hostname zůstal celou dobu homeassistant (jde jen o dočasný mDNS-published suffix). Loki retention a čitelnost journalu citelně zaneseny. Konfliktní adresa byla vždy 192.168.20.7 — tj. LAN announcement vracející se přes IoT linku.

Oprava

Odebrání IoT (vtnet3 / OPT2) z Avahi reflectoru: pfSense GUI → Services → Avahi → Settings → odškrtnout IoT interface → Save. avahi-daemon.conf je auto-generovaný balíčkem („Do not edit this file" — přímá editace přes SSH se při příštím save přepíše), proto jen GUI. Verifikace: count_over_time konfliktů spadl z ~37 700/h na 0 do několika sekund po restartu avahi. Rollback: zaškrtnout IoT zpět.

Lessons / follow-up

  • Proč tahle cesta a ne HA-side ha network update enp0s19 --mdns off: odebrání z reflectoru zachová HA nativní mDNS announce+resolve na IoT VLAN (ESPHome/zeroconf discovery nových zařízení dál funguje), zatímco --mdns off by ho uřízl. Navíc opravuje latentní bug — IoT zařízení teď resolvují homeassistant.local na správnou 192.168.40.7 (dříve reflector cpal do IoT VLAN .20.7, což je cross-VLAN firewallem blokovaná adresa).
  • Vědomý trade-off: IoT mDNS je teď plně izolovaný od ostatních VLAN. Cross-VLAN discovery IoT zařízení napřímo po .local (mimo HA, mimo CUPS/AirPrint proxy na debian-services) přestane fungovat — IP funguje dál. Pokryto: Aqara FP2 ap. → HA na IoT nativně; tiskárna → AirPrint announce z debian-services (LAN). Reflector zůstal pro LAN↔GUEST↔MEDIA (AirPlay nedotčen, viz docs/technologie_domu/media.md).
  • Obecné pravidlo: mDNS reflector + multi-homed host = riziko conflict loopu. Při přidávání dalšího dual-homed stroje do reflektovaných VLAN tohle ověřit.

2026-06-12 — Journal pipeline do Loki byl 3 měsíce mrtvý (Alloy override config)

Příčina

Vlastní override config Alloy addonu (alloy/config.alloy) měl v loki.source.journal dvě chyby proti configu, který si addon generuje sám:

  1. Chyběl path = "/var/log/journal" — bez něj Alloy hledá journal podle machine-id svého kontejneru a tiše čte nula řádků. Host journal je do kontejneru mountnutý právě na /var/log/journal (addon option journald: true).
  2. forward_to směřoval přes loki.relabel.journal komponentu — relabel pravidla se ale aplikují už u zdroje (relabel_rules); druhý průchod běží až po zániku __journal_* metadat, regex "(.*)" matchne prázdný řetězec a labely unit/app/hostname smaže.

Důsledky

Od 2026-03-17 12:07 do 2026-06-12 nebyly v Loki žádné logy HA Core, addonů ani Z2M (job="journal" neexistoval). Nikdo si toho nevšiml — žádný alert na výpadek log source neexistuje a log_monitor.py prázdný výsledek journal query interpretuje jako „čisto". Diagnostický klíč: Alloy web UI hlásil komponentu „healthy", ale metrika loki_source_journal_target_lines_total = 0 (healthy ≠ čte data).

Oprava

Dva commity do alloy/config.alloy (8993271, c9fdca5): doplněn path, forward_to přepojen rovnou na loki.write.endpoint.receiver. Po restartu addonu journal teče s plnými labely. Rollback: revert commitů + restart addonu.

Lessons / follow-up

  • „Healthy" stav Alloy komponenty znamená jen „běží" — při debugu vždy číst metriky (:12345/metrics).
  • Při override configu addonu vždy diffovat proti generovanému configu addonu (rootfs/etc/cont-init.d/alloy_setup.sh v repu addonu).
  • Korekce dřívějšího závěru: pfSense filterlog do Loki teče (~1,7M řádků/den, 97 % celkového objemu), jen bez hostname/app labelů, takže je nedohledatelný. Gotcha „filterlog neteče" v .agent/memory/MEMORY.md byla mylná — opravena.
  • Celkový review loggingu 2026-06-12 odhalil další otevřené body (retence Loki vypnutá, žádné freshness alerty, stale dashboardy, RFC5424 zdroje bez hostname) — viz todos.md sekce Monitoring Stack.

2026-06-12 — TČ v Energy dashboardu: Riemann integrace + proporční breakdown po službách

Rozhodnutí

Spotřeba TČ pro HA Energy se počítá Riemann integrací okamžitého příkonu (reg 2193, polling 30 s), ne z energetických counterů TČ. Breakdown DHW / bazén / topení vzniká proporčním rozdělením 2193 podle okamžitých tepelných výkonů služeb (reg 2187/2188/2189); zbytek (standby, čerpadla) jde do bucketu „Other", takže buckety sedí přesně na celek. Implementace: modbus/ecoforest.yaml + template.yaml (ecoforest_electric_power_*) + nový sensor.yaml (ecoforest_electric_energy_*).

Důvod

TČ nemá lifetime kWh registr ani per-service elektroměr — jen denní/měsíční/roční countery, které se resetují. Reset + jediný vadný read přes Modbus bránu (192.168.40.34, historicky náchylná k timeoutům) by total_increasing statistiku napočítal dvakrát; Riemann integrace je vůči výpadkům čtení robustní (jen po dobu výpadku neroste). Denní counter 2206 se čte pouze jako diagnostika/cross-check. Proporční split je korektní aproximace, protože kompresor obsluhuje služby sekvenčně.

Vědomá omezení

  • Bazénovou VZT (DG1) ani podlahu baz. haly (SG3) nelze oddělit od topení domu — z pohledu TČ je to jedna služba „heating", okruhy berou z akumulačky bez měření. Jemnější rozpad = kalorimetry na okruzích (zamítnuto, overengineering bez rozhodovacího use-case).
  • Hodnoty jsou interní odhad TČ (z inverteru), ne fakturační měření.
  • Spotřebu klimatizací (Daikin) přes Modbus číst nelze — EKMBDXA/DIII protokol energetická data vůbec nepřenáší (ověřeno proti celému manuálu). Případné řešení = podružné měření na přívodu venkovní jednotky (např. Shelly Pro EM).

Operační dopad

4 nové buckety v Energy dashboardu (Individual devices); provozní popis pro rodinu: docs/technologie_domu/vzduchotechnika/tepelne_cerpadlo.md §„Spotřeba TČ v HA Energy dashboardu", technický detail: docs/manualy/ecoforest_ecogeo/modbus.md.


2026-06-12 — HACS závislosti dashboardů + Aseko integrace (evidence)

Rozhodnutí

Evidujeme externí HACS závislosti, na kterých stojí konfigurace, aby budoucí „odkud se to tu vzalo / můžu to smazat" mělo odpověď:

  • expander-card (Alia5/lovelace-expander-card) — rozbalovací sekce na Klima dashboardu (dashboards/klima.yaml). Bez ní se karty zobrazí jako „Custom element doesn't exist".
  • AlexxIT/WebRTC Camera — two-way audio doorbellu v dashboardu (viz doorbell sekce v MEMORY.md).
  • scrypted-advanced-notifier / @scrypted/mqtt — doorbell → MQTT → HA most.
  • Aseko bazén — integrace přes HACS aseko_local (lokální, ne cloud) → senzory chemie/zdraví bazénu.

Důvod

Tyto integrace nejsou v repu (HACS custom_components/ je gitignorovaný), takže z konfigurace samotné není poznat, že na nich něco závisí. Bez evidence hrozí, že se po čisté reinstalaci HA „ztratí" a dashboardy/funkce se rozbijí bez zjevné příčiny.

Operační dopad

Po reinstalaci HA / migraci nutno tyto HACS balíčky doinstalovat, jinak: Klima dashboard rozbitý (expander-card), doorbell bez two-way audia (WebRTC), bazénová chemie bez dat (aseko_local). Operační detaily expander-card: docs/chytry_dum/home_assistant/dashboardy.md.


2026-06-12 — WiFi: AP8 carport, NP2 kanály pinned, doc_gen_unifi na API-key auth

AP8 carport přidán (192.168.20.18)

Venkovní U7 Pro Outdoor (UAPA6B0) adoptován, pfSense rezervace .18 (hostname ap8-carport), uplink USW-48-PoE port 8 (1 Gbps, PoE 25.5 W). AP2 (U6-LR, .12) je záměrně odpojen — čeká na přemístění; v controlleru zůstává adoptovaný.

Radio fix: Min RSSI off + NP2 kanály fixní

Rozhodnutí: AP1 měl na 2.4 GHz zapnutý per-radio Min RSSI (-75) → vypnuto. AP6+AP7 běžely na auto kanálech a RRM je posadil do co-channel kolize na obou pásmech (2.4 ch11 + 5G ch48) → kanály zafixovány: AP6 = 11/48, AP7 = 6/44. NP2 je tím čisté: 2.4 GHz 1/6/11 (AP1/AP7/AP6), 5 GHz 36/44/48. Důvod: Min RSSI kick je dokumentovaný anti-pattern pro Apple klienty (wifi.md). Auto-RRM se ukázal jako nespolehlivý u sousedních AP — fixní kanály jsou deterministické. Jak: Přímo přes classic API (PUT /rest/device/<id> s radio_table), MCP server zápis rádií neumí. Ověřeno live po reprovisioningu.

doc_gen_unifi.py: API-key auth + síťový watchdog

Rozhodnutí: Skript přepsán z user/password loginu (účet už neexistuje → 403) na API klíč UNIFI_MCP_API_KEY z .env (header X-API-KEY) — tentýž credential, který používá UniFi MCP server. Zamítnuta varianta „skript volá MCP": vrstva navíc nad stejným HTTP API, MCP toolset má díry (žádné get/setting, stat/rogueap, zápis rádií) a závislost na third-party balíčku. Rozšíření: watchdog celé sítě — offline zařízení, firmware drift, roaming assistant na WLAN, auto-kanály, airtime, uplink speed, port flapping/errors/STP, PoE budget, weak clients, kanálové konflikty obou pásem. Issues sekce je v generovaném dokumentu první. Pozn. k Network 10.x API: rest/device (GET) a stat/event s API klíčem nefungují (404); konfigurace zařízení vč. port_overrides je ale celá ve stat/device.

Nálezy z prvního běhu watchdogu (k řešení)

  • Roaming Assistant zapnutý na WLAN úrovni u SSID wifi, wifi guest, media (5 GHz, -75 dBm) — vypnout (viz todos).
  • sw-dilna port 7: 4650× link down (flapping) + chyby; port 5: 165k TX errors při 10M linku — prověřit kabeláž v dílně.

Druhé kolo (téhož dne)

  • AP5 a AP8 kanály zafixovány na aktuální hodnoty (AP5 = 6/36, AP8 = 6/40) — žádná RF změna, jen vypnutí auto-RRM. Všech 7 online AP má teď fixní kanály.
  • „Remote syslog vypnutý" byl false positive watchdogu — settings klíč je rsyslogd, ne rsyslog (bug zděděný ze staré verze skriptu). Syslog je zapnutý (192.168.20.20:514) a v Loki ověřeno, že logují všechna AP i oba switche. Skript opraven.
  • Cross-check UniFi vs. pfSense rezervace přidán do watchdogu (check_pfsense_reservations, pfSense v2 API, auto-skip bez PFSENSE_API_KEY), ne do check_consistency.py — ten je záměrně offline CI gate. První běh: všechna adoptovaná zařízení rezervace mají.

Roaming Assistant vypnut na všech SSID (třetí kolo)

Rozhodnutí: WLAN-level Roaming Assistant (min-RSSI kick, -75 dBm na 5 GHz) vypnut na wifi, wifi guest, media přes rest/wlanconf. Důvod: V hustém pokrytí 7 AP s 802.11v/r a laděnými výkony je benefit nulový (Apple klienti roamují sami), zatímco kick škodí na venkovní hraně dosahu (zahrada/auto u AP8 — žádné lepší AP neexistuje, klient se dostane do kick-reconnect smyčky) a u stacionárních zařízení na media (AirPlay dropouty). Korekce historie: Dřívější zdůvodnění ve wifi.md („No Internet / DNS drop po roamingu") mylně přisuzovalo Min RSSI symptom, jehož skutečnou příčinou byly out-of-sync MAC tabulky mezi dvěma switchi (FDB problém, vyřešen 2026-02 konsolidací AP na jeden USW-48-PoE). wifi.md opraveno — zdůvodnění odděleno od incident-specific poznatků; kanálová tabulka aktualizována na pinned plán.


2026-06-10 — GitOps pipeline: cílený reload místo plošného restartu + alerting

Rozhodnutí

Auto-sync pipeline (GitHub → HA) nově aplikuje změny podle typu: no_action (docs, skripty, dashboardy, AppDaemon, Z2M/Alloy configy) · reload (automatizace, scripts, scény, template, input_*, rest, blueprints → homeassistant.reload_all, bez výpadku) · restart (configuration.yaml, modbus/, climate.yaml a vše neznámé → plný restart, povolen kdykoliv). Selhání pipeline nově alertuje na mobil.

Důvod

Původní automatizace „Restart After Success" restartovala celý dům po každém konfiguračním commitu, proto byla v březnu 2026 vypnutá — od té doby se změny aplikovaly jen ručně, což tiše porušovalo předpoklad „dům se aktualizuje sám". Navíc měla design umožňující restart-smyčku (trigger to: success bez resetu status souboru) a auto_sync.sh uměl při rozbitém git fetch navždy tiše hlásit „up to date". Reload pokrývá ~80 % běžných commitů bez výpadku, takže auto-aplikace už není obtěžující a lze ji mít zapnutou. Smyčka je vyloučena designem: automatizace status resetuje na idle dřív, než akci provede.

Operační dopad

  • Commit do dashboardů/docs = žádná akce; běžné YAML = reload bez výpadku; core soubory = restart HA (~1–2 min).
  • Nové entity: binary_sensor.ha_gitops_stale (nasazený commit ≠ GitHub), automatizace HA GitOps - Apply Synced Changes a HA GitOps - Failure Alert (mobil: vadný commit hned, fetch fail >20 min, stale >30 min).
  • Změny zigbee2mqtt/configuration.yaml vyžadují ruční restart Z2M addonu (HA restart je neaplikuje).
  • Plný popis a troubleshooting: docs/infrastruktura/servery/homeassistant.md, sekce GitOps.

2026-06-10 — Canon GX7040: oprava DNS + síťové hardening

Root cause „nemůže se připojit k internetu"

Tiskárna hlásila chybu připojení k internetu, přitom lokální tisk (CUPS přes IPP) fungoval — proto byl výpadek neviditelný. Příčina: DNS bylo ručně nastavené na 0.0.0.0 (manual setup). Cloud služby Canonu (a SMTP) nerozresolvovaly. Oprava: DNS přepnuto na Auto setup (přebírá z DHCP → pfSense 192.168.40.1). Proxy = Do not use, IP/gateway v pořádku (DHCP rezervace 192.168.40.8).

Lesson: lokální IPP tisk maskuje výpadek WAN/DNS na tiskárně. Monitoring přes HA IPP integraci (stav + inkousty) odhalí unavailable dřív, než to zjistí uživatel u tiskárny.

Síťové hardening — vypnuté nepoužívané discovery/print protokoly

CUPS na debian-services tiskne výhradně přes ipp://192.168.40.8/ipp/print (ověřeno CUPS web UI). Proto vypnuto:

  • WSD (Windows discovery) — nikdo netiskne přímo z Windows do IoT VLAN.
  • LPD/LPR (515), RAW (9100), LLMNR — CUPS je nepoužívá.

Ponecháno zapnuté: IPP (631), Bonjour/AirPrint, SNMPv1 (community public, read-only — pro monitoring). Ověřeno z LAN po změně: 631 succeeded, 9100 + 515 refused, tisk funguje. Rollback = checkboxy zpět v Remote UI → System info → LAN → Advanced setup. IP/IPsec filtering ponecháno vypnuté — segmentaci řeší pfSense (IoT izolovaná od LAN).

Scan-to-email: přímé SMTP místo Canon cloudu

Canon cloud scan-to-email má limit 4 strany → nepoužitelné. Řešení: přímý SMTP (Printer settings → Set mail server), který limit nemá (strop = velikost přílohy mailserveru). Příjemci pro víc lidí přes Scan(Email direct from device) adresář. IJ Cloud Printing Center záměrně neregistrováno (cloud závislost + zdroj přesně té chyby s internetem). Chyba 3414 = nenastavený mail server, zmizí po vyplnění SMTP.

Doc fix

firewall.md uvádělo IP tiskárny 192.168.40.9 — reálně 192.168.40.8 (souhlasí inventory + DHCP rezervace). Opraveno.


2026-06-02 — Zahradní domek: detail střešního přesahu, rozteč latí, mřížky

Stav stavby: skelet + OSB opláštění stěn i záklop střechy hotové, přesah OSB ~150 mm všemi směry (víc než původně plánovaných 60 mm). Dveře na východ, spád na západ.

Rozhodnutí

1. Wraparound přesahu místo open-eave s čelním prknem dokola. Střešní folie se zahne kolem hrany OSB na spodní stranu přesahu a shingle-lapem překryje stěnovou folii (souvislá obálka). Podhled tvoří plotovky 120×19 (stejný variabilní rastr jako stěny) šroubované přímo přes folii do OSB, bez provětrávací latě — na podhled neprší ani nesvítí, mezery 10–15 mm mezi plotovkami k odvětrání stačí; větraná mezera je detail pro plochy exponované dešti/UV.

2. Hrany přesahu — dvě dýchají, dvě se zavírají. Mezera kontralať+lať pod plechem (~80 mm) je průtočný kanál podél spádu: zadní hrana (okap, západ) = sání → větrací okapní pás (hřebínek proti ptákům), přední hrana (hřeben, východ) = výdech → čelní prkno se štěrbinou ~20 mm pod odsazenou/perforovanou ukončovací lištou. Boky (podél spádu) se uzavírají: krajní kontralať na hraně OSB + čelní prkno KVH 120×19 (skladba čela ~117 mm ≈ výška prkna 120 — sedí). Uzavřít všechny čtyři hrany = mrtvá kapsa, kondenzát zpod plechu nemá kam schnout.

3. Ventilační štěrbina stěnového rastru: 15–20 mm mezi horní hranou stěnových plotovek a podhledem. Výstup 40mm větrané mezery za stěnovým rastrem (vzduch → prostor pod podhledem → ven spárami podhledu). Kritické nezalepit/nedotáhnout prkna na doraz.

4. Rozteč vodorovných latí stěn ~550 mm (původně ~400 mm). KVH prkno 19 mm s 1 vrutem na lať potřebuje podporu max. ~600 mm; pod 500 mm je zbytečně hustě. 4 latě na zadní/boční stěnu, 5 na přední. Spodní lať 50–80 mm nad hranou OSB (fixuje folii), horní těsně pod věncem.

5. Ventilační mřížky podél spádu: výdech východ (přední) nahoře, sání západ (zadní) dole. Revize původního jih/sero návrhu (solární zisk na jih je reálný, ale druhořadý). Osa Z→V vyhrává ze dvou důvodů, které se sčítají: (a) šikmý strop pultovky žene teplý vzduch do nejvyššího (východního) koutu → výdech patří tam; (b) převažující západní/jihozápadní vítr v ČR → západní stěna návětrná (přetlak na sání), východní závětrná (podtlak na výdech), vítr posiluje komínový tah. Háček: západní sání je na návětrné straně (hnaný déšť) → žaluziová mřížka + nad zónou odstřiku. Pokud lokální vítr (plot, terén) míří jinam, řídit se realitou pozemku.

Zamítnuté alternativy

  • Smrková prkna 19×100 místo KVH 60×40 na latě/kontralatě střechy — kontralať 19 mm = poloviční větraná mezera (minimum 40 mm); samořez plechu 4,8×35 by 19mm latí téměř prošel; tenký smrk praská při šroubování přes EPDM podložky. Úspora ~1 500 Kč nestojí za kompromis.
  • Provětrávací lať pod podhledem přesahu — overkill (viz bod 1), navíc by zvýšila čelo o 40 mm a komplikovala krytí.

2026-06-01 — Meteo / srážkoměr: plán Ecowitt WS90 Powered by Shelly (Zigbee), odloženo

Rozhodnutí

Pro venkovní meteo (srážky + vítr + UV + solar) cílit na Ecowitt WS90 Powered by Shelly spárovaný přímo do stávajícího Zigbee koordinátoru přes Z2M nebo ZHA — bez Ecowitt RF gateway, bez Shelly hubu. Nákup odložený, dokud nedozraje HA integrace.

Důvod

  • WS90 je 7-in-1 senzor: piezo srážkoměr (bez pohyblivých dílů), ultrazvukový anemometr, UV, solar radiation, teplota/vlhkost. Pokrývá víc než samostatný WH40, navíc bez mechaniky k zanášení.
  • Shelly varianta používá standardní Zigbee 3.0 místo Ecowitt sub-GHz RF, takže odpadá samostatná gateway. Bluetooth slouží jen pro initial pairing.
  • Alternativa klasická WS90 + GW2000 je dnes vyzrálá, ale přidává krabičku navíc a uzamyká do Ecowitt ekosystému. Pokud později budou potřeba další Ecowitt senzory (WH51 záhony, WH57 blesky, WH55 leak), gateway přidám tehdy — ne preventivně.
  • Pro vyhřívání anemometru v zimě (proti sněhu/námraze) plánovat 12 V / 1 A power adapter + extension cord k WS90; solar topení neuživí.
  • Umístění: zahradní domek (rozhodnuto 2026-06-17). Skrz stěnu jde jen 12 V DC napájecí kabel pro vyhřívání anemometru — síťový adaptér 230 V zůstává uvnitř, ven jde nízké napětí. Žádný datový kabel skrz zeď — data jdou bezdrátově přes Zigbee do koordinátoru. Průchodku dimenzovat na rezervu (vodotěsnost + tah, řeší se venkovní prostředí, ne síťové napětí).

Co blokuje nákup

  • Z2M issue #31070: WS90 se spáruje, ale hodnoty se neaktualizují po počátečním reportu (chybný attribut reporting binding).
  • ZHA quirks issue #4558: chybí decoding pro vítr (rychlost/směr/gust), UV index a srážky — tj. právě ty senzory, kvůli kterým WS90 kupuju.
  • Bez fixu by senzor v HA reportoval jen teplotu/vlhkost = drahý teploměr.

Trigger pro nákup

  • Z2M issue #31070 closed/merged a komunitní zpráva potvrzuje, že hodnoty se aktualizují, NEBO
  • ZHA quirks #4558 merged a wind/UV/rain atributy mapované.

Stav 2026-06-18 — trigger SPLNĚN, nákup odblokován

  • Z2M (náš stack, SLZB-MR3): WS90 device page má dnes plnou podporu — dekóduje vítr (speed/dir/gust), UV, srážky, teplota/vlhkost, osvit, baterii + odvozené (rosný bod, wind chill, rain rate…). Tím padá obava z „drahého teploměru".
  • #31070 closed jako completed (2026-05-11): „hodnoty se neaktualizují" nebyl z2m software bug, ale firmware WS90. Řešení potvrzené v komentářích: před párováním (nebo po něm s re-pair) aktualizovat firmware přes Bluetooth/Shelly app, pak reporting stabilní. Pozor: stížnosti na stabilitu u fw 1.1.6 → po nákupu nahrát nejnovější fw a ověřit aktualizaci hodnot.
  • #4558 closed jako completed (2026-04-04): ZHA quirk merged přes PR #4708, dodáno v HA 2026.4.0 (funguje bez custom quirku). Relevantní jen jako pojistka, kdyby se přešlo z Z2M na ZHA.
  • Reziduální riziko = firmware reporting stabilita, ne decoding. Mitigace levná (BT firmware update). Nákup proto reálně možný hned.

Operační dopad

Žádný okamžitý — designové rozhodnutí pro budoucnost. Položka v todos.md sleduje stav obou issues. Pokud do ~12 měsíců nedozraje, přehodnotit (fallback: klasická WS90 + GW2000).

Vyloučené alternativy

  • Hydreon RG-15 (optický): jen srážky, accuracy horší než tipping-bucket, komunitní podpora slabá.
  • Davis Vantage Pro2: 15k+ Kč, profi liga, overkill pro homelab.
  • Netatmo srážkoměr: cloud-dependent, proti self-hosted filozofii.
  • Klasická Ecowitt WS90 + GW2000: funkční dnes, ale druhá gateway navíc; lépe počkat na čistší Zigbee path, dokud reálně nepotřebuju i jiné Ecowitt senzory.

2026-06-01 — Zahradní domek: fasádní folie Swisstherm Plus + detail spodní hrany

Rozhodnutí

Pod variabilní svislý rastr (mezery 10–15 mm mezi prkny) i pod střešní trapézový plech použít Swisstherm Plus 120 g s integrovanou páskou (milpe.cz, ~63 Kč/m², role 1,5 × 50 m). Spodní hranu folie NElepit k OSB butylem — řešit mechanicky tlakem spojní vodorovné latě + shingle lap přes čelo OSB + soklová perforovaná lišta.

Důvod — folie

  1. Otevřený rastr s mezerami 10–15 mm a ~13 % otevřené plochy vyžaduje fasádní folii certifikovanou pro otevřený rainscreen. Běžné střešní/fasádní DHV (Jutadach 135, Delta-Vent S) mají certifikaci typicky do 5 mm spár → tvůj případ překračuje limit. Swisstherm Plus je certifikovaný do 20 mm spár a 30 % otevřené plochy — komfortně pokrývá.
  2. UV stabilita za otevřeným rastrem trvale. Černá PE 0,2 mm by zkřehla za 2–4 roky kvůli UV skrze mezery.
  3. Integrovaná páska na hranách role řeší horizontální překryvy → odpadá samostatná role akrylové pásky na ně.
  4. Difúzní otevřenost zajistí odvod stavební vlhkosti z OSB ven přes větranou mezeru 40 mm. Funguje i po budoucím dodatečném zateplení.
  5. Cenová pozice: ~63 Kč/m² vs. Tyvek UV Facade ~280 Kč/m². Pro nezateplenou stodolu je Swisstherm Plus právě správná úroveň — Tyvek je premium overspend.

Důvod — detail spodní hrany (bez butylu)

Konflikt dvou požadavků: (1) voda zvenku za folii ne, (2) případný kondenzát/zafouklá vlhkost ven ano. Plné lepení butylem k čelu OSB splní (1) ale poruší (2). Mechanická fixace tlakem spodní vodorovné latě řeší obojí: folie pod laťí přitištěná po celé délce → těsné, pod laťí volně končí shingle lapem → kondenzát vykápne ven nad EPDM. Soklová perforovaná lišta uzavírá detail pohledově a brání hmyzu/myším.

Nákup

  • 1× Swisstherm Plus 1,5 × 50 m s integrovanou páskou — ~4 700 Kč (milpe.cz)
  • 1× Jutadach SP SUPER 50 × 25 m (univerzální fasádní akrylová) — ~360 Kč — vertikální spoje folie, ventilační mřížky, rezerva do dílny
  • Butyl páska — NE, nepotřeba
  • Soklová perforovaná lišta RAL 7016 — ~600–1 000 Kč — klempířský prvek (ne lepicí páska)

Celkem foliový systém: ~5 060 Kč.

Iterace v rámci rozhodnutí

Cesta k finálnímu výběru prošla několika kroky, které se vyplatí mít zaznamenané pro budoucí podobné detaily:

  1. Černá PE 0,2 mm (původně) → zamítnuto kvůli UV za otevřeným rastrem.
  2. DHV Jutadach 135 / Delta-Vent S → zamítnuto, certifikace nepokrývá spáry > 5 mm.
  3. Tyvek UV Facade → správný produkt pro dům, overspend pro stodolu (2× cena Swisstherm).
  4. Swisstherm Plus 120 g → finální volba (správná úroveň, certifikace pokrývá, integrovaná páska).
  5. Butyl na spodní hraně → původně doporučeno, korigováno: mechanická fixace pod laťí je správný detail (odráží původní záměr v docs/projekty/zahradni_domek.md před touto iterací).

2026-05-28 — Tepelné čerpadlo Ecoforest ecoGEO: dokumentace + read-only Modbus

Kontext

Zdokumentován realizační projekt vytápění (Veskom 24-2-0019) a navázáno Modbus mapování TČ Ecoforest ecoGEO B1 T5-22 HTR EH (zemní, 4 vrty). TČ má dva komunikační kanály: EasyNet (správa instalatéra) a paralelní BM2 RS485 Modbus port přes Waveshare bránu 192.168.40.34:502 do HA.

Stávající modbus/ecoforest.yaml byl spekulativní a nefunkční: senzory četly 0, switch.ecoforest_heat_pump byl ve skutečnosti coil 53 "control by BUS" (ne on/off), demand selecty zapisovaly reálné požadavky.

Rozhodnutí

  1. Fáze 1 = striktně read-only. Přepsán konfig na ověřenou register mapu (komunitní bp-ouhaha/EcoForest-modbus-registers + Ecoforest Modbus 2021), INPUT registry (FC04), int16 + scale 0.1, core set + alarm/demand/season discrete inputs. Žádné write entity. Řízení (setpointy pro FTV přebytky, dynamická křivka) je odložená Fáze 2 (vyžaduje "control by BUS", obchází část interlocků → až po testu v servisním okně).
  2. Zimní program natrvalo. Control manuál potvrdil: Summer blokuje celý režim HEATING (i bazénovou halu); Winter povoluje HEATING+DHW+POOL. Sezónnost domu řešit přes Stop temperature for heating (globální práh pro všechny topné skupiny) + pokojové termostaty, ne přepínáním na léto.
  3. FTV patrony EP1/EP2 jsou řízené přímo z HA (Waveshare relé modbus-rele-topeni), ne přes Ecoforest E-manager jak předpokládal projekt. Strategie přebytků: nejdřív kompresor (COP 3–5) přes setpointy, patrony až overflow.

Nález — špatná verze register mapy (vyřešeno → HP24)

Diagnostika (poller zastavený, čistý probe): brána OK (Modbus TCP→RTU, 19200 8N2), odpovídá slave 17, ale staré adresy (1–200/5000+, input FC04) vracely 0. Příčina: tahle jednotka používá mapu „HP24" (verze 24) — vše v HOLDING registrech (FC03), rozsah 2000+ (zdroj: TapHome template ecoforest-hp24). Stará komunitní mapa pro tuhle firmware generaci neplatí. Konfig přepsán na HP24 (venkovní 2080, DHW 2130, akumulačka 2132, program 2044, výkony 2186/2188/2193, patrony coily 2046/2047, …).

Blokace — fyzický RS485 (vyřešeno) → menu selektor BUS_HP24

Při ověřování instalatér zkoušel přehodit A/B a „vrátit". Slave 17 pak přestal odpovídat úplně; uživatel rozdělal TČ a našel jeden Modbus drát vypadlý ze svorkovnice — po vrácení slave 17 zase odpovídá. Kabel je tedy OK.

Zbývající blokace: HP24 registry (2000+) vrací „Illegal Data Address". Empiricky regulátor servíruje jen rozsah ~0–440 (samé 0), nic na 2000+. Po vyřešení od instalatéra (oficiální Ecoforest dokument „Modbus variables list HP24 V01.00 EN", str. 1) vyšlo najevo, že firmware v24 (WWD24 = ecoGEO B&C) podporuje DVĚ variabilní mapy (HP23 starší, HP24 nová) a jedna se vybírá v Installer Menu → Remote Control → „Selección BUS" mezi BUS_HP23 / BUS_HP24. Aktuálně je TČ na HP23 (nebo na žádné), proto HP24 adresy neexistují. Akce na instalatérovi: přepnout selektor na BUS_HP24. Pak HA konfig (přepsaný podle oficiálu) okamžitě začne číst.

Korekce — TapHome měl místy špatně

Oficiální Ecoforest dokument vyvrátil dvě věci, které měla TapHome HP24 šablona špatně:

  • Tlaky mají gain 0,1 (/10), ne 0,01 (/100).
  • Aux topidla 2046–2050 jsou holding registry s 0/1 (typ A), NE coily (TapHome měl prefix C:). Pět aux registrů, ne dva: 2046 integrated, 2047 external generic, 2048 DHW aux (EP1), 2049 BUFFER aux (EP2), 2050 chiller. Adresy 2048/2049 přesně odpovídají patronám EP1 a EP2, které v tomto domě HA řídí přes Waveshare relé — TČ teď bude umět vidět, kdy jsou patrony aktivní.

Strategie — Fáze 2 přes registr 2775+2777

Oficiální mapa odhaluje elegantnější cestu pro FTV: 2775 Surplus control enabled by BUS + 2777 Surplus regulation setpoint (kW). HA řekne TČ „máme N kW přebytku" a TČ si samo nastaví výkon kompresoru — nemusí HA psát do jednotlivých setpointů. To je preferovaná cesta pro Fázi 2.

RESOLUTION (2026-05-28 večer) — BMS port byl v MB Slave místo MB Extended

Po dlouhém debugu se ukázalo, že selektor BUS_HP24 byl správně (instalatér), ale BMS port (kam fyzicky vede gateway) byl nastaven na MB Slave, ne na MB Extended. V základním režimu port odpovídá, ale neservíruje rozšířenou variabilní mapu (HP24 registry 2000+ vrací Illegal Data Address). Symptom přesně sedí: 0–~440 valid ale samé 0, 2000+ illegal. Uživatel přepnul Installer Menu → Externí řízení → Řízení BUS → BMS konfigurace → Protocol z MB Slave na MB Extended + power-cycle TČ jističi 60 s → data tečou živě (venkovní 17,8 °C, DHW 43,2 °C, tlaky 1,4/1,3 bar — sedí na EasyNet).

Lessons learned:

  • Selektor BUS_HP24 (verze mapy) a MB Extended (režim portu) jsou dvě nezávislá nastavení — obě musí být současně. Instalatér měl první, ne druhé.
  • Český překlad firmwaru duplikuje název „řízení požadavku" pro dvě různé položky (Services control + BUS control) — orientovat se podle obsahu, ne podle názvu.
  • Reg 2185 (power units) u téhle jednotky = 10 = W; výkony bereme bez scale, unit W.
  • Pool temperature 2134 = 0 (TČ nemá zapojené čidlo bazénu, řízeno externě přes DI4); v configu zakomentováno.
  • Pool setpoint 2151 ≠ cílová teplota bazénu — je to delivery T výstupní vody z TČ do výměníku (~40 °C). Cíl bazénu je externí termostat.

Po power-cyclu se objevil „Inverter Alarm 50" na displeji TČ; Modbus reg 2065 vrací alarm_level = 0 → alarm se nejspíš sám resetoval během bootu (běžné u invertorové elektroniky po power-cyclu).

Důvod

Bezpečnost a reverzibilita: read-only nemůže nijak zasáhnout do živého řízení domu. Mapování je deterministicky ověřitelné, jakmile instalatér zpřístupní data.


2026-05-25 — Klimatizace: cooling-only architektura a master cool guard

Kontext

Audit implementace odhalil tři bugy v appdaemon/apps/daikin_modbus.py a climate.yaml:

  1. Fan speed se čte/zapisuje do bitů 1..3 (reserved) místo 14..12 (per Daikin EKMBDXA manuál) → fan v UI nereflektuje realitu a změna nemá efekt.
  2. Fan control flag (bity 7..4 holding 42001) se nenastavuje na 6 — gateway ignoruje fan příkazy. force_control_nibble: 6 v apps.yaml bylo zakomentované.
  3. Mode write na slave jednotky (1-00..1-08) vrací "illegal data" — DIII master/slave systém vyžaduje mode write jen na master (1-09 Janča, jižní pokoj). Slaves mají holding 42002 = 6 (Dependent) a kopírují master's mode automaticky.

Dodatečné nálezy:

  • Madoky fyzicky neexistují. Dodací list R-24000204 uvádí 10× BRC1H52W, ale realizace je bez nich. Jediný kontrolér je Modbus + servisní technik.
  • Uživatel deklaroval, že systém bude vždy chladit, nikdy topit.

Rozhodnutí — cooling-only architektura

Zjednodušení místo komplikovaného master/slave routingu:

  1. Mode register se po inicializaci NIKDY nezapisuje z runtime cesty. HA píše jen on/off + setpoint + fan per jednotka.
  2. Master 1-09 (Janča) je zamčený v cool přes:
  3. Startup guard (30 s po naběhnutí AppDaemonu): ověří master mode = 2; pokud ne, zapíše.
  4. Periodický guard každých 10 minut: stejná kontrola + zápis + persistent notification (klima_master_guard).
  5. Slaves automaticky chladí, protože přebírají master's mode přes DIII-Net hardware.
  6. HA climate entity nabízí jen modes: ["off", "cool"]. set_hvac_mode("cool") se interně přeloží na power=on (žádný mode write). set_hvac_mode("off") na power=off.
  7. Pokud někdo (servisní zásah) přepne master jinam, guard to do 10 min přepíše zpět + uživatel dostane persistent notification.

Rozhodnutí — UX optimalizace

  • Optimistický refresh: po každém Modbus zápise AppDaemon volá homeassistant.update_entity na 4 sensory cílové jednotky s odstupem 1.5 s a 8 s. UI reaguje do 1-2 s místo dosavadních 30 s polling intervalu.
  • Debounce: identické zápisy do 2 s se zahodí. Chrání před 7000 commands/year per unit limitem + před uživatelským spamem.
  • Dashboard Klima: přidán souhrnný panel "Stav klim" (kolik běží, master mode + setpoint), per pokoj velký thermostat card s viditelným mode + setpoint, vedle něj termostat na zdi.

Důvod

Nejjednodušší možné řešení splňující všechny požadavky:

  • Master/slave routing v AppDaemon = řádově složitější kód, který by jen obcházel hardware design.
  • Cooling-only předpoklad eliminuje 90 % mode logiky.
  • Guard se chová jako self-healing safety net pro neočekávaný vstup zvenku.
  • Madoky neexistují = ve skutečnosti není kdo by mode změnil, takže guard je jen safety net (low frequency, ne hot path).

Implementace

  • appdaemon/apps/daikin_modbus.py: kompletní refactor — fix bug #1 (bity 14..12), bug #2 (vždy fan_control_flag=6), startup+periodic guard, optimistic refresh, debounce.
  • appdaemon/apps/apps.yaml: přidáno master: "1-09", guard_interval_s: 600, debounce_s: 2.0.
  • climate.yaml: 10 single + 1 group entity přepsány na modes: ["off","cool"], fan read fix (bity 14..12), set_hvac_mode jen on/off.
  • dashboards/klima.yaml: souhrnný panel + thermostat cards per pokoj.

2026-05-25 — Klimatizace: provozní strategie a skupinové ovládání

Kontext

10 Daikin split jednotek (1-00..1-09, plus aggregát obýváku climate.daikin_room_a_1_01_1_03). V létě obrovský FTV přebytek. Rekuperace Atrea DUPLEX 560 má v létě výměník jen 0,7 kW — dům neuchladí, není její role. Trvale puštěné "noční předchlazení" přes bypass je kontraproduktivní (přes den táhne horký vzduch dovnitř).

Rozhodnutí — provozní režim klim

  • Nepouštět klimy v režimu AUTO trvale. V přechodném období AUTO flip-flopuje topení/chlazení, energetická pohroma + nepředvídatelný komfort.
  • Léto + PV přebytek: Daikiny v COOL módu, ne AUTO. Setpoint 23–25 °C pro běžný provoz, 22 °C pro PV soak.
  • Drž klimy zapnuté přes celý solární peak (~10–18 h) — inverter moduluje na nízkém výkonu, dům akumuluje chlad do tepelné hmoty (beton, omítky, nábytek), uvolňuje se do večera. To je „zadarmo chlad z FTV".
  • Po západu slunce vypnout (nebo zvedat setpoint vysoko) a větrat okny — venku chladněji než uvnitř, klima v noci by jela ze sítě/baterie zatímco výsledek je k mání zadarmo přes komínový tah.
  • Neobývané zóny vypnout — 10 jednotek na low-load má nezanedbatelný sumární standby.

Rozhodnutí — climate group: scripty, ne HACS

Pro skupinové ovládání 10 jednotek odmítnuty cesty: a) HACS climate_group (dependency, single point of failure, průměrná current_temperature matoucí), b) nativní HA group: helper (neumí předat setpoint/HVAC mode). Zvolena cesta scriptů v scripts.yaml — KISS, žádná dependence, v repu, předvídatelné. Skupinové scripty (klima_off, klima_cool_24, klima_cool_25, klima_pv_soak) cílí 9 jednotek bez ložnice. Ložnice (climate.daikin_1_00) má vlastní presety (18/20/22), aby šla agresivně chladit nezávisle.

Rozhodnutí — HS portál a velkoplošná okna přízemí jako blocker klimy

Otevřený HS portál (a obecně velkoplošné posuvné dveře / okna v přízemí) přes horký den s běžící klimou = řádově 3–10 kW tepelného zisku za hodinu z venkovního vzduchu. Klima v té zóně se tím anuluje. Pravidlo: buď otevřeno NEBO klima, ne obojí. V noci s venkovní < vnitřní teplotou naopak chtěné (komínový tah).

V HA je k dispozici jeden souhrnný signál binary_sensor.dilna_sensor_3 (Modbus relé v dílně, série NC kontaktů) = "Okruh okna přízemí". Logika NC: ON = vše zavřeno, OFF = aspoň jedno otevřeno. Nerozlišuje konkrétní okno; pro orchestrátor stačí, pravidlo zní stejně bez ohledu na zdroj otevření.

Důsledek pro návrh

  • Vytvořen dashboard dashboards/klima.yaml (Klima panel) — teploty + skupinové ovládání + stav otvírání.
  • Orchestrátor okna ↔ klima jako další iterace (viz todos.md), využije scripty + dilna_sensor_3 + weather.forecast_home + PV přebytek.

2026-05-14 — Dílna: standardní překližky a materiál pracovní desky

Náhrada 12 mm CP/C 237479 → MULTI BB/CP 113089

Demos drží 237479 (PV SUR BŘÍZA CP/C 1250×2500×12) v katalogu jako "Na objednávku, cenu nutno ověřit" — de-facto signál EOL bez oficiálního ohlášení. Náhrada 113089 PV SUR BŘÍZA MULTI BB/CP 1250×2500×12 (skladem, 1 858,90 Kč bez DPH, BFU 100, 9 vrstev).

Volba MULTI BB/CP místo levnější MULTI CP/CP (210702, na objednávku, 1 802 Kč): rozdíl 56 Kč/ks (3 %) je v rámci šumu, BB face dává znatelně lepší pohledovou stranu (drobné oválné záplaty místo větších záplat a suků z CP), CP back na nepohledové straně stačí. Skladová dostupnost vs. čekání na objednávku rozhodla.

Analogicky migrováno 18 mm: 18 mm CP/C → 113093 MULTI BB/CP 1250×2500×18 (skladem, 2 582,59 Kč bez DPH).

Materiál pracovní desky dílenského stolu: MPX bříza 40 mm (210713) místo buku spárovky

Stůl 2500 × 800 × 40 mm. Volba 210713 PV SUR BŘÍZA CP/C 1250×2500×40 (5 299 Kč bez DPH, skladem) proti 76251 SPAR BUK A/B 4000×800×40 NAPOJ (5 836 Kč bez DPH, 2-10 dní z centrálního skladu).

Důvody pro MPX:

  1. Dimenzionální stabilita. Buk spárovka má sezónní pohyb 3-5 mm na 800 mm šířky → vyžaduje uchycení přes drážky a táhla, jinak praská nebo se kroutí. MPX se nehne.
  2. Žádné finger-joints jako slabé body. Linkovaný buk je napojovaná spárovka (lamely nastavované cinkovým spojem). Pod úderem kladiva, dláta nebo padajícího kusu oceli praskne ve fingerjointu dřív než ve dřevě. MPX je homogenní.
  3. Žádná údržba olejem/voskem. Buk vyžaduje 1-2× ročně, MPX 0× (případně lokální přebroušení po 5 letech, nebo polyuretan).
  4. Rozměrový fit. Z 1250×2500 vyřízneš pás 800×2500 na desku + zbude užitečný odřezek 450×2500. Z buku 4000×800 zbude 1500 mm jako odpad.
  5. 40 mm v jednom kuse u obou — výhoda buku (jeden masivní kus) odpadá. MPX 40 mm nemusíš lepit doubled.
  6. Cena/m² nižší o 7 % + skladem.

Volil bych BUK jen pokud stůl ≥ 3 m (MPX bys musel napojovat), případně dominantně ruční nářadí + důraz na repairabilitu hoblíkem a klasický look. Pro tuhle dílnu (kombinace ruční + elektro, nevytápěná část roku, KISS údržba) MPX vyhrává.


2026-05-11 — pmx1 stejný upgrade + debian-services NFS mount race fix

Co se stalo

  • pmx1 upgradován stejně jako pmx2 (kernel 6.17.13-7-pve pin, ZFS 2.4.1, pve-manager 9.1.9). Reboot ~4 min, vše naběhlo čistě.
  • Po rebootu pmx1 šly debian-services Docker služby do stavu "Up, ale prázdné" — nginx, grafana, n8n, cups, influxdb v restart loopu. Důvod: NFS mount /mnt/data z TrueNAS 192.168.20.4:/mnt/tank/app-data při boot LXC 107 selhal s mount.nfs: Network is unreachable, ačkoliv network-online.target byl aktivní o sekundu dříve. Bind mounty v Docker kontejnerech se navázaly na prázdné adresáře, takže nginx neměl conf.d, grafana/n8n/influxdb neměly data dir.
  • Důsledek: ha.marada.name, wifi.marada.name, data.marada.name byly z internetu nedostupné ~10 min, než se na to přišlo. Direct IP přístup (192.168.20.7:8123 apod.) fungoval celou dobu — bypass nginxu.

Příčina

/etc/fstab měl NFS line s prostými defaults:

192.168.20.4:/mnt/tank/app-data /mnt/data nfs defaults 0 0
Bez _netdev systemd automaticky vygenerovanou mnt-data.mount unit neoznačil za network-dependent, mount se zařadil do local-fs.target a vykonal se ještě před tím, než síťová vrstva LXC fakticky resolvovala TrueNAS přes ARP. network-online.target v LXC kontejnerech reportuje "online" eagerně podle stavu interface, ne podle reálné reachability.

Důležitý poznatek: NFS mezi debian-services (.20) a TrueNAS (.4) NEPROCHÁZÍ přes pfSense — oba endpointy jsou na VLAN 20, stejný L2 subnet. Mount má fungovat i s pfSense down, problém byl čistě v LXC systemd timing, ne v síťové cestě.

Oprava

  1. /etc/fstab na debian-services:
    192.168.20.4:/mnt/tank/app-data /mnt/data nfs _netdev,nofail,x-systemd.mount-timeout=60,noatime 0 0
    
  2. _netdev → systemd zařadí do remote-fs.target + Wants=network-online.target + After=network-online.target
  3. nofail → boot pokračuje i kdyby NFS selhal (žádný emergency mode), ale Docker pak nenastartuje (záměr)
  4. x-systemd.mount-timeout=60 → 60s na první mount attempt
  5. docker.service drop-in /etc/systemd/system/docker.service.d/10-wait-for-nfs.conf:
    [Unit]
    RequiresMountsFor=/mnt/data
    After=mnt-data.mount
    Requires=mnt-data.mount
    
  6. systemctl daemon-reload. Dependency graph ověřen.
  7. Reboot-test 2026-05-11 ~11:03: LXC 107 rebootnut z pmx1 (pct reboot 107). Boot timeline ukázal správné pořadí: nfs-client.targetremote-fs-pre.targetremote-fs.targetmnt-data.mountdocker.service. Všech 7 kontejnerů startovalo až po úspěšném NFS mountu, žádné restart loopy. ha.marada.name / wifi.marada.name / data.marada.name po ~60 s vrátily 200/302. Fix potvrzen funkční.

Operační dopad

  • Příští reboot pmx1 / LXC 107 už by neměl shodit nginx + Docker stack na debian-services.
  • Pokud TrueNAS NFS dlouhodobě nedostupný, Docker se sám nestartne (s nofail) — boot LXC ale projde čistě, dá se diagnosticky pracovat. To je správné chování (lepší než dnešní "půl-startovaný stack s prázdnými mounty").
  • Backup: /etc/fstab.bak.20260511-085833 na debian-services.

Lessons / follow-up

  • Pro každý NFS/CIFS/jiný síťový mount v /etc/fstab na libovolné VM/LXC v homelabu vždy _netdev,nofail + explicit systemd dependency od služby, která mount potřebuje. Dnes není zmapováno, kde všude jinde tohle můžeme mít rozbité — viz follow-up.
  • Validace fixu vyžaduje reboot LXC 107, ne jen daemon-reload. Plánovat při příštím přirozeném okně (další pmx1 reboot / plánovaný upgrade).
  • network-online.target v LXC nereflektuje plnou L3 reachability — eagerně reportuje "online" jakmile rozhraní existuje. Pro NFS na stejném subnetu je to obvykle dost, ale s race podmínkou se to dnes ukázalo. Jiné scénáře (RDS, cross-VLAN routing) by mohly selhat ještě výrazněji.

2026-05-10 — pmx2 kernel soft lockup ve ZFS, upgrade kernelu + ZFS, čistka cluster ghost

Příčina

Kernel 6.17.9-1-pve + ZFS 2.4.0-pve1 na AMD Ryzen 5 7430U trefily race v smp_call_function_many_cond (cross-CPU IPI při TLB flush) během ZFS writeback. systemd-journal zavolal fsync → ZFS zpl_fsyncfolio_clear_dirty_for_io → flush_tlb_mm_range → IPI gate, na kterém se zaseklo CPU#5 (544 s) i CPU#3 (637 s). Klasický ZFS-on-Linux + nový kernel + AMD timing bug, ne chyba konfigurace.

Důsledky

      1. 2026 06:10:21 — poslední log na pmx2, kernel zombie (jádra v IPI deadlocku, ale ne plný panic)
  • corosync ztratil totem (Totem is unable to form a cluster), pvescheduler šel do "no quorum"
  • HAOS VM 102 zamrzla s celým hostem → HA, doorbell, automatizace, MQTT mimo provoz ~3.5 h
      1. 2026 09:48:12 — pmx2 sám rebootnul (pravděpodobně softdog watchdog po extrémně dlouhém timeoutu nebo kernel.panic auto-reboot, jakmile se zhroutilo dost subsystémů, aby se kernel dostal k panic handleru). Uživatel nebyl doma, HW nikdo nereset, UPS-backed = power glitch vyloučen.
  • Při bootu HAOS přehodil primary NIC zpět na enp0s19 (známý gotcha z 2026-05-06)

Pmx1 stejný HW + stejný buggy mix, jen "vyhrál loterii" — uptime 69 dní bez incidentu. Ne fix, jen statistika.

Oprava

  1. apt dist-upgrade na pmx2 — kernel 6.17.13-7-pve, ZFS 2.4.1-pve1 (kernel modul i userspace), pve-manager 9.1.9
  2. Pin kernelu před rebootem: proxmox-default-kernel 2.1.0 táhne závislost na proxmox-kernel-7.0 (nový major branch). Pinnul jsem 6.17.13-7-pve přes proxmox-boot-tool kernel pin 6.17.13-7-pve && proxmox-boot-tool refresh, aby v produkci zatím nebootoval 7.x.
  3. Reboot ~100 s, zpool healthy, všechny VM/LXC nahoru, HAOS NIC tentokrát rovnou na enp0s18 (race se neprojevil — neopravoval jsem, není to fix)
  4. Cluster cleanup: pve ghost member (z éry před migrací na ZFS, kdy se pve přejmenoval na pmx1 se stejnou IP 192.168.20.2 — duplicitní záznam v corosync.conf zůstal). pvecm delnode pve z pmx2, Config Version 5 → 6, Expected votes 3 → 2. Backupy v pmx2:/root/corosync.conf*.bak.20260510-112246.

Operační dopad

  • Cluster je teď čistý 2-node (expected_votes=2, quorum=2). Ztráta jednoho zdravého nodu = read-only stav (VMy běží, ale žádné start/stop/migrate/backup). Stejné riziko jako předtím (s ghostem fakticky 2/3 = taky no quorum při ztrátě jednoho), jen teď čistě reportované.
  • pmx1 zatím nepatchnut — stejný buggy mix, stojí v řadě po 24–48h pozorovacím okně pmx2.
  • Pin politika: po každém upgradu před rebootem zkontrolovat proxmox-boot-tool kernel list a explicitně pinovat zamýšlenou verzi, dokud neproběhne přechod na PVE 9.x (nebo cokoliv, co kernel 7.x dováží).

Lessons / follow-up

  • Soft lockup v IPI/TLB během ZFS writeback je race condition, ne deterministický bug. Bez upgradu se trefí znovu — možná za týden, možná za půl roku. Druhý node se stejným HW = stejný risk → upgrade musí dorazit i na pmx1.
  • Watchdog se nakonec uplatnil, ale s 3.5h zpožděním. Stojí za zvážení agresivnější kernel.hung_task_panic=1 + kernel.panic=10, aby se zombie stav konvertoval na rychlý reset. Otevřená otázka, ne automaticky → viz todos.md.
  • 2-node cluster bez qdevice = žádná quorum redundance. Kvůli homelab roli zatím akceptovatelné, ale stojí za zvážení (debian-services LXC nebo TrueNAS jako 3. arbiter hlas) → viz todos.md.
  • HAOS dual NIC primary fix se po tomto rebootu nepotřeboval, ale to není dostatečný důvod fix nepsat — byla to race, příště se zase přehodí. Položka v todos.md z 2026-05-06 zůstává otevřená.

2026-05-08 — Zigbee coordinator: CC2674P10 → EFR32MG24, clean reset

Rozhodnutí

  • Přejít z TI CC2674P10 (Z-Stack adapter) na Silicon Labs EFR32MG24 (EmberZNet adapter) jako primární Zigbee coordinator. Oba čipy fyzicky na SLZB-MR3 (smart switching role v UI, žádný HW swap).
  • CC2674P10 přepnut do role "Thread RCP" / Matter-over-Thread (zatím nevyužito, k dispozici pro budoucí Matter zařízení).
  • Síť kompletně přebudována od nuly — všech 39 zařízení re-paired. Cross-stack migrace zstack→ember coordinator_backup.json není v Z2M podporovaná (zigbee-herdsman ember adapter explicitně rejectuje, viz EmberAdapter.getStoredBackup).

Důvody

  • CC2674P10 měl recurring silent stalls: 4× SLZB-MR3 reboot v rozmezí 5.–6. 5. 2026, mezitím Z2M MQTT bridge reportoval connected, ale Zigbee rádio nepřijímalo zprávy. Auto-restart automatika zigbee_auto_restart_z2m_when_bridge_offline nezabírala (čekala na bridge offline, který nenastal). Detail viz Z2M issue #28197 a HA Community thread o frequent adapter resets.
  • Side benefit clean resetu: vygenerován nový náhodný network_key. Předchozí síť běžela na default well-known klíči Z2M 01030507090b0d0f00020406080a0c0d (z dokumentace examples), kdokoliv s Zigbee snifferem v dosahu mohl dešifrovat veškerý provoz. Tento dlouhotrvající security issue byl tímto vyřešen.
  • Friendly_names a HA entity IDs zachovány — Z2M matchuje device_id podle IEEE v configuration.yaml, takže po re-pairu se entita objeví se stejným ID.

Trade-off

  • ~37 zařízení × ručně přepárovat. Akceptovatelné, protože téměř vše je mains-powered (7× nástěnný termostat = routery, ~30× mmWave presence senzory = mains, 1× battery sensor zahrada). Žádné lezení na strop k battery zařízením.
  • Ghost devices v configu (47 entries bez friendly_name, dlouhodobí offline duchové) při této příležitosti smazány z zigbee2mqtt/configuration.yaml. Z2M při startu publishl prázdné MQTT retained payloads → HA discovery entity zmizely.

Lessons learned z migrace (2 hodiny zoufalství než přišel průlom)

1. Z2M addon ukládá serial port/baudrate mimo configuration.yaml

Addon má vlastní options storage (HA UI → Add-ons → Zigbee2MQTT → Configuration tab). Při startu přepíše serial sekci v configuration.yaml z těchto options, ne naopak. Hodiny jsme měnili port/baudrate/adapter v repu, addon to při každém startu rolloval zpátky. Smoking gun byl řádek z debug logu Z2M:

"serialPort":{"path":"tcp://192.168.40.2:7638",...}
…zatímco YAML v repu měl 6638. Fix: nastavit serial v addon UI, ne v YAML.

2. SLZB-MR3 TCP porty jsou per-fyzický-chip, ne per-role

  • EFR32MG24 → port 6638 (vždy, bez ohledu na roli)
  • CC2674P10 → port 7638 (vždy)
  • Když se v UI prohodí role (MG24 = "Zigbee Coordinator", CC2674P10 = "Thread RCP"), porty se nehýbou. Z2M Config generator v SLZB UI ukazuje správnou kombinaci pro daný chip.
  • Boot log [EFR32MG24] Started server on port: 6638 to potvrdí. Při debug se na něj koukat.

3. Z2M ember adapter rejectuje zstack coordinator_backup.json

Konkrétní check v zigbee-herdsman/src/adapter/ember/adapter/emberAdapter.ts:

if (!data.stack_specific?.ezsp || !data.metadata.internal.ezspVersion) {
    throw new Error("[BACKUP] Current backup file is not for EmberZNet stack.");
}
Backup ze Z-Stacku má stack_specific.zstack + metadata.internal.znpVersion → fail. Cross-stack migrace bez re-páru není možná v aktuální verzi Z2M (community workaround "stejný network_key v configu" je nespolehlivý a nezachová se s addon UI options).

4. Ghost devices v configu jako symptom zaseknutí mesh

Po roky se nahromadilo 47 entries bez friendly_name ('0xHEXIEEE': friendly_name: '0xHEXIEEE') — zařízení, která se kdysi pokusila joinit, ale nikdy se neusadila / nebyla pojmenovaná / odpojila se navždy. Při migraci ideální moment vyčistit. Pravidlo: pokud friendly_name == IEEE, je to ghost.

5. Diagnostika: ASH RSTACK probe je definitivní test chipu

Když Z2M dostává Received frame with CRC error ve smyčce, problém může být v Z2M, v SLZB proxy, nebo v chipu. Nezávislý test:

printf '\x1A\xC0\x38\xBC\x7E' | nc -w 3 192.168.40.2 6638 | xxd
Zdravý ember chip odpoví 1A C1 02 0B 0A 52 7E (Cancel + RSTACK + ASHv2 + reset cause SOFTWARE + CRC + flag). Pokud probe vrátí čistý RSTACK ale Z2M reportuje CRC error, problém je v Z2M nebo addon konfiguraci.

Aktuální provozní stav (po migraci)

  • MG24 firmware: 20260416 DEV (SDK 9.0.1.0). Stable 20250212 jsme vyzkoušeli oba — chovaly se stejně (problém byl v addon options portu, ne firmware). Doporučení do budoucna: až SMLIGHT vydá nový stable, vrátit se na něj.
  • Z2M config: port: tcp://192.168.40.2:6638, adapter: ember, baudrate: 115200, rtscts: false, transmit_power: 5, channel: 25.
  • Forensic kopie v /config/zigbee2mqtt/: coordinator_backup.json.zstack-old, database.db.zstack-old, state.json.zstack-old, database.db.backup.zstack-old.

Pravidla pro budoucí Z2M / coordinator migrace

  • Addon options jsou autoritativní pro serial config, YAML je sekundární — vždy ověř UI před debugem proxy/firmware.
  • Cross-stack migrace přes backup file = re-páření (alespoň v Z2M 2.10.x + zigbee-herdsman 10.0.7).
  • Default Z2M network_key je security risk — po každém clean network setupu ověř, že Z2M vygeneroval random key (ne 01030507...). Při legacy síti naplánuj rotaci.
  • Z2M syslog → Loki je rozbitý (separate TODO) — během migrace přidat file do log_output, ať máš lokální debug log na disku.

2026-05-06 — Zahradní domek: EPDM pod prahem + spodní detail OSB

Rozhodnutí

  • Mezi spodní práh (KVH 120×60 impregnovaný) a dlažbu vložit EPDM pásek 60 mm × 3–5 mm.
  • Spodní hrana OSB 15–20 mm nad dlažbou (≈10 mm nad horní hranou EPDM), nikoliv 5 mm jak původně zvažováno.
  • PE folie shingle lap přes čelo OSB a hranu prahu, končí volně nad dlaždicí (≥30 mm pod hranou OSB), nelepit těsně po celé délce k prahu.
  • Čelo OSB napustit napouštědlem před montáží.

Důvody

  • Kapilární přerušení: dřevo prahu nesmí přímo lízat dlažbu — i impregnace má omezenou životnost. EPDM nesaje a ruší vzlínání.
  • Odstřik vody dopadající na dlažbu sahá až ~20–30 cm vzhůru. 5 mm spára by hranu OSB udržela trvale vlhkou; čelo OSB saje jak houba.
  • Folie shingle lap = voda po folii teče ven (přes hranu prahu pryč), ne pod OSB. Bodové prolepy páskou (ne souvislé) nechávají kondenzátu cestu ven.

Trade-off

  • O 15–20 mm vyšší spodní hrana OSB znamená, že stavba je o tolik víc exponovaná zezdola — proto je nutný sokl/lišta co nejdřív po zaplášťování (ideálně před první zimou), ne až jako finiš.

Lessons / pravidla pro budoucí venkovní stavby na dlažbě

  • KVH na dlažbě vždy přes EPDM/lepenku, i když je impregnovaný.
  • OSB čelo nikdy v zóně odstřiku bez krytí folií + následně lištou.
  • Spodní hrana foliového pláště drenážuje, netěsní — voda musí mít kudy ven.

2026-05-06 — Doorbell incident: HA OS upgrade přehodil primary NIC

Příčina

HA OS upgrade 17.1 → 17.2 (slot A → B) v noci na 2026-05-05 po rebootu přehodil v NetworkManageru primary: true z LAN NIC enp0s18 (192.168.20.7) na IoT NIC enp0s19 (192.168.40.7). HA tím začala posílat veškerý non-local traffic ze zdroje 192.168.40.7.

Důsledky (časová osa)

  • 2026-05-05 04:54 UTC sensor.doorbell_call_statusunavailable. REST poll na 192.168.60.10 z IoT NIC narazil na pfSense pravidlo Block IoT to Internal Networks (vtnet3 rule "OPT2 → Private_Networks") → drop bez logu (filterlog do Loki neteče). Tím padla i ring detection a rest_command.doorbell_open_gate (timeout 5 s).
  • HomeKit ring přes Scrypted (192.168.20.21) fungoval, protože šel přes jiný zdroj — proto symptom "zvonek zvoní na hodinkách, ale HA nedělá nic".
  • Po opravě primary zpět na enp0s18 zůstalo druhotné: HA dál posílala Cast zařízením streamovací URL http://192.168.40.7:8123/... z paměti — Cast (VLAN 50) na .40.7 nedosáhne (VLAN 50 → VLAN 40 pravidlo neexistuje), TTS "Někdo zvoní u dveří" nehrálo.

Oprava

  1. Primary NIC zpět na LAN: v HA UI (Settings → System → Network) workaround "změnit IP a zpět" donutil Supervisor přepsat NM connection a primary skončil na enp0s18. Přímo vymazat gateway u IoT NIC v UI nešlo (validace).
  2. Restart HA Core (ha core restart) smazal cache zdrojové IP pro Cast → URL pro Cast pak generována správně z 192.168.20.7.

Lessons learned a prevence

  • Po každém HA OS upgrade: zkontrolovat ha network info že primary: true zůstalo na enp0s18 (LAN). Pokud se přehodilo, uplatnit fix postup výše.
  • Preventivní pinning: nastavit explicitní route metric, aby budoucí upgrade nepřepočítal pořadí — viz follow-up v todos.md.
  • pfSense filterlog do Loki neteče — diagnostiku síťových dropů lze dělat jen lokálně přes SSH (pfctl -ss, clog /var/log/filter.log). Follow-up: zapojit pfSense syslog do Loki, viz todos.md.
  • Bug v pfSense pravidle Block MEDIA to Internal Networks (vtnet4): source je 192.168.50.1 (gateway IP) místo <OPT3__NETWORK> — pravidlo je no-op. VLAN 50 → ostatní privátní VLANy tak NENÍ blokovaná. Pokud má opravdu blokovat, opravit source. Follow-up v todos.md.

2026-05-06 — Tuya mmWave: form factor wins over reliability

Rozhodnutí

Zůstat u Tuya 24G mmWave senzorů (~30 ks v domě) navzdory endemickému bugu occupancy DP stuck off. Hardwarová migrace na Aqara FP2 / FP1E / podobné odmítnuta.

Důvod

  • Form factor je primární kritérium. Tuya MTG235-ZB-RL (flush-mount, kruhové pouzdro do sádrokartonu) i hranatá nástěnná varianta zmizí v architektuře. Aqara FP2 byl označen jako "hodně nesystémová" — viditelné plastové pouzdro je vizuálně rušivé.
  • Aqara FP2 zůstává jen tam, kde jeho velikost nevadí — 3 ks v obýváku/Relax, kde je velký otevřený prostor a smysl dává zonal detection (gauč × jídelní stůl × průchozí zóna).

Hardware varianty v provozu

  • Flush-mount (zapuštěná do stropu, sádrokarton): Tuya MTG235-ZB-RL "24G Human presence sensor with relay" (TZE200/TZE204). Použito např. WC, Spíž. Kratší horizontální dosah (kužel dolů).
  • Surface-mount (přisazená na zeď, hranaté pouzdro): Tuya "2.4G/5.8G MmWave radar". Použito např. Technická. Delší dosah, scanuje celou místnost.
  • Obě Tuya varianty mají stejný occupancy-DP-stuck-off bug, ale jiné Z2M mappingy.
  • Aqara FP2 (WiFi, ne Zigbee) — jen v obýváku/Relax pro zonal detection.

Coping mechanism

Default: software workaround, ne hardwarová výměna.

  • Robust template pattern: binary_sensor.presence_<area>_robust ORuje native occupancy s target_distance > 0 (případně illuminance fallback)
  • AppDaemon listener tweaks
  • Z2M device reconfigure když firmware DP stuck

ESPHome LD2410/LD2412 ve vlastním malém pouzdře jsou přijatelná alternativa, pokud jde o flush-mount realizovat. Jinak hardware swap je last resort — framing "až když budeš rekonstruovat tuhle místnost", ne "měl bys vyměnit".


2026-04-27 — AI stack: zjednodušení na n8n + MCP

Dify.ai, AnythingLLM, OpenClaw zrušeny

Rozhodnutí: Stack pro "Rodinného Asistenta" zúžen na n8n (orchestrace + AI nodes) + MCP servery (kontextová vrstva) + Gemini Flash (LLM). Dify.ai (LXC 108) decommissionovaný. AnythingLLM a OpenClaw vypadly z plánu úplně. Důvod: Dify duplikoval funkcionalitu, kterou pokrývá kombinace n8n AI nodes + MCP servery. RAG nad volnými dokumenty (knowledge base) byl jediný feature, který MCP nepokrývá — ale 99 % domácích AI dotazů jsou strukturovaná data (HA stav, logy, repo dokumentace), kde je MCP přesnější než vektorový search. Kompromis: ad-hoc tool pro PDF lookup pokud opravdu vznikne use case (faktury, manuály), místo celého RAG frameworku. Dopady: docs/chytry_dum/ai_stack.md přepsán; TODOs Dify.ai a AI Knowledge Base v todos.md zrušeny; LXC 108 dify plánovaná dekomise (PBS zálohy slabé — poslední 2026-02-18 — ale nejsou závislosti, riziko nízké).


2026-04-27 — UniFi: migrace na UniFi OS Server + MCP přístup

UniFi OS Server jako primární controller

Rozhodnutí: Nasazen samostatný UniFi OS Server (Network App 10.3.55) na 192.168.20.5:11443 jako VM 109 na pmx1 (2 vCPU, 4 GB RAM, 30 GB ZFS, cloud-init, onboot=1, MAC bc:24:11:78:4e:7d). Všech 9 zařízení (7 AP + USW-48-PoE + USW-Lite-8-PoE) je adoptováno na nový controller a ONLINE. Důvod: UniFi OS Server poskytuje oficiální API (Integration v1) s API key auth, což je výrazně bezpečnější než legacy session login na původním LXC 101. Zároveň je to maintained aktivně Ubiquiti, zatímco klasická Network Application v Java na LXC se postupně utlumuje. Stav legacy LXC 101 (192.168.20.6:8443): Destroyed (2026-04-27) včetně unreferenced disků. PBS zálohy zachovány (15 denních, retence dle backup jobu). Single point of failure pmx1: UniFi OS Server VM 109 nemá ZFS replikaci na pmx2 — výpadek pmx1 znamená ztrátu UniFi managementu (data plane na AP/switch zůstává s last-known configem). Mitigace: PBS restore (~10 min), případně budoucí ZFS replikace 109 → pmx2.

MCP přístup přes enuno/unifi-mcp-server

Rozhodnutí: Použít enuno/unifi-mcp-server v0.2.4 (Apache 2.0) přes uvx, autentizace přes oficiální UniFi API key (UNIFI_MCP_API_KEY v .env). Konfigurace v .mcp.json jako unifi. Důvod: Oficiální API key (scopable, revocable) místo storage admin user/pass v env. 74 MCP tools, confirmation u mutujících operací (sedí s AGENTS.md "Safety before speed"). Alternativa sirkirby/unifi-network-mcp má víc tools (161), ale primárně user/pass auth — odmítnuto. Ověřeno: Curl proti /proxy/network/integration/v1/sites|devices|clients vrací HTTP 200, klíč má read přístup k požadovaným endpointům.

Skripty (backup_unifi.py, doc_gen_unifi.py, sync_pfsense_unifi.py) zatím nepřevedeny

Rozhodnutí: Skripty zůstávají napojené na legacy .20.6 přes user/pass — refactor na nový API key + endpoint /proxy/network/integration/v1/ je samostatný úkol (viz todos.md). Důvod: Skripty zálohují/synchronizují prázdný legacy controller, což je tichá ztráta zálohy. Riziko: pokud je backup_unifi.py v cron/scheduleru, pravidelně produkuje useless artefakt. Nutno migrovat dřív, než dojde k decommissi LXC 101.

Nginx wifi.marada.name ještě cílí na legacy

Pozn.: docs/files/docker/debian-services/nginx/conf.d/wifi.conf proxiuje https://192.168.20.6:8443. Updates vyžaduje koordinovaná změna nginx config + redeploy — viz follow-up.


2026-03-11 — Konsolidace logování

rsyslog relay zachován

Rozhodnutí: Zachovat rsyslog relay na debian-services pro RFC3164 zařízení (TrueNAS, UniFi APs, Aruba switche). Důvod: Alloy addon na HAOS neumožňuje snadno vystavit další porty. Přechod na přímé RFC3164 by vyžadoval fork addonu nebo komplexní override config. Stávající relay funguje spolehlivě. Poznámka: Alloy loki.source.syslog teoreticky podporuje syslog_format="rfc3164", ale praktická implementace na HAOS addonu je příliš invazivní.

Smazání loki_search.py a check_homelab.py

Rozhodnutí: Smazat oba skripty. Nahrazeny Grafana MCP tools (query_loki_logs, list_loki_label_names, query_loki_stats, query_loki_patterns) a GitHub MCP. Důvod: MCP nástroje poskytují stejnou funkcionalitu přímo v AI agentu bez nutnosti údržby Python skriptů. Pro CLI ad-hoc dotazy stačí Grafana UI.

Grafana dashboardy v repu s auto-provisioningem

Rozhodnutí: Dashboard JSON soubory verzovány v docs/files/docker/debian-services/grafana/dashboards/, Grafana je automaticky načítá přes file-based provisioning. Důvod: Dashboardy musí být součástí repa, aby šly reprodukovat a reviewovat. Auto-provisioning zajišťuje synchronizaci.

Grafana MCP: oprava .env načítání

Rozhodnutí: Grafana MCP server v .mcp.json spouštěn přes sh -c ". .env && exec uvx mcp-grafana" místo přímého uvx mcp-grafana. Důvod: ${GRAFANA_TOKEN} env var nebyla dostupná v prostředí MCP procesu, protože .env se nenačítala. Shell wrapper to řeší.


2026-03-11 — Monitoring Stack Revize

CheckMK → Uptime Kuma

Rozhodnutí: Nahradit CheckMK za Uptime Kuma. Důvod: CheckMK je enterprise IT nástroj; naše potřeba je "upozorni když něco spadne." Uptime Kuma pokrývá HTTP/TCP/ICMP health checks a push notifikace bez operační complexity. CheckMK Fáze 3+4 nebyly nikdy dokončeny — signal overengineeringu.

InfluxDB 1.8 → 2.x

Rozhodnutí: Zahodit InfluxDB 1.8 (stará data jsou zašuměná), nasadit InfluxDB 2.x čistě. Důvod: InfluxDB 1.8 je EOL, jen InfluxQL, žádná auth, žádné retention policies v UI. HA nativně podporuje v2.

Grafana verzování a provisioning

Rozhodnutí: Pinout Grafana verzi (přejít z latest), dashboardy uchovávat jako JSON v repozitáři přes Grafana provisioning. Důvod: latest tag způsobuje neočekávané breaking changes při každém docker compose pull. Dashboardy v repozitáři jsou verziovatelné a opakovatelně nasaditelné.

Automation vrstva

Rozhodnutí: Vybrat jednu automation vrstvu (n8n nebo Python skripty), ne obojí bez jasného rozdělení rolí. Stav: Nerozhodnuto — vyžaduje přehled co n8n a Python skripty aktuálně dělají.

rsyslog relay

Rozhodnutí: Ověřit zda Alloy zvládne RFC3164 přímo (parametr syslog_format). Pokud ano, relay na debian-services eliminovat. Stav: Neověřeno — akce v todos.

Celková filozofie stacku

Závěr: Stack trpí "AI-assisted sprawl" — každá konverzace přidala nástroj bez celkového kontextu. Jádro (Proxmox/ZFS/pfSense/TrueNAS/PBS/Tailscale/HAOS/Zigbee2MQTT) je solid. Okolní vrstvy (monitoring, metrics, automation) potřebují konsolidaci.


2026-03-10 — Velux KLF200 Recovery Incident

Symptom

Velux integrace v Home Assistant přestala fungovat. Custom komponenta opakovaně hlásila chyby při čtení dat zařízení a následně už se na 192.168.40.10:51200 nedařilo připojit.

Pozorování z logů

  • custom_components.velux opakovaně hlásila Error fetch limitation data for cover #...
  • následně se v logu objevovalo Unable to connect to KLF200: [Errno 111] Connect call failed ('192.168.40.10', 51200)
  • watchdog automatizace velux_klf200_auto_recovery se spouštěla, ale končila na template condition ještě před switch.turn_off

Rozhodnutí

Watchdog zůstává postavený na TCP healthchecku + power cyklu KLF200 přes smart plug + reload integrace, ale rate-limit logika byla opravena tak, aby se automatizace sama neblokovala.

Skutečná závada recovery

Podmínka v velux_klf200_auto_recovery kontrolovala state_attr(..., 'current') == 0, zatímco automatizace běží v mode: single. Tím si běžící instance sama zablokovala pokračování a recovery se nikdy nedostala k power cyklu ani reloadu integrace.

Nejpravděpodobnější příčina primárního incidentu

Nejlepší současná hypotéza je, že po sérii restartů Home Assistantu zůstaly v KLF200 staré session / TCP spojení, gateway narazila na svůj interní limit a nová spojení pak odmítala. To odpovídá pozorovanému connection refused na portu 51200.

Důsledek pro provoz

  • problém nebyl jen v samotném KLF200, ale i v rozbité self-healing vrstvě
  • od commitu 6faca8a by recovery měla znovu skutečně dojít až k power cyklu a reloadu integrace
  • při další podobné sérii restartů je stále vhodné omezit zbytečné reconnect stormy a sledovat, zda KLF200 znovu nezačne odmítat nová spojení

2026-03-10 — Home Assistant Areas a Labels

Rozhodnutí

Area v Home Assistantu mají reprezentovat větší, stabilní a lidsky srozumitelné prostory. Jemnější lokální logika řízení, zejména pro presence a světla v rámci členitých místností, se nemá modelovat dalším drobením Area, ale pomocí skupin, helperů a automatizačních zón.

Důvod

Příliš jemné Area zhoršují přehlednost, komplikují přiřazení zařízení ovlivňujících větší celek a vedou k modelu, který je spíš odrazem dosahu čidel než skutečné struktury domu. Stabilnější Area lépe odpovídají lidskému ovládání, dashboardům a budoucímu floorplanu.

Labels

Labels mají sloužit pro průřezové role a vlastnosti napříč místnostmi, ne jako druhá hierarchie polohy. Prakticky: Area odpovídá na otázku „kde to je“, zatímco Label odpovídá na otázku „co to je / jakou to má roli“.

Důsledek pro návrh

  • koupelny a WC mají být obvykle samostatné Area, pokud jsou to samostatné a běžně rozlišované místnosti
  • otevřené nebo členité prostory mají mít spíše větší Area, případně rozdělení jen tam, kde to dává smysl i lidsky a provozně
  • budoucí jemné chování presence a lokálního osvětlení má vzniknout nad stabilními Area, ne přestavbou area modelu

2026-03-08 — TrueNAS, AppArmor, WiFi

TrueNAS: Chybějící IPv4 Gateway

Problém: TrueNAS neměl nastavený IPv4 default gateway (ipv4gateway prázdný v middleware konfiguraci). Žádný internetový provoz nefungoval. Fix: midclt call network.configuration.update '{"ipv4gateway": "192.168.20.1"}' + gai.conf IPv4 preference + Init script pro persistenci.

AppArmor: swtpm & dhclient DENIED

Problém: AppArmor na PVE blokoval swtpm (chybějící capability sys_admin) a dhclient (unix socket protocol match limitace). Fix:

  • swtpm: Přidáno capability sys_admin do /etc/apparmor.d/local/usr.bin.swtpm
  • dhclient: Přepnut do complain mode (flags=(attach_disconnected,complain)) — známá AppArmor limitace s unix dgram protocol=0 Ověření (2026-03-09): Na PVE je /{,usr/}sbin/dhclient skutečně v complain mode (aa-status + /etc/apparmor.d/usr.sbin.dhclient). Přetrvávající DENIED záznamy pro dhclient jsou audit hlášky v complain mode a mají být považovány za monitoring noise, ne za aktivní blokující incident.

WiFi DNS Timeouty: Monitoring Noise

Rozhodnutí: DNS timeout, IP timeout a ACL reject hlášené UniFi AP (STA-TRACKER) jsou monitoring noise, ne reálné DNS selhání. Důvod: pfSense Unbound běží, DNS firewall pravidla pro všechny VLAN existují, resoluce funguje. AP pouze reportuje krátké prodlevy při roamingu/probuzení.


2026-02-18 — Apple DHCP Reconnection Failure (VYŘEŠENO)

Symptom

Po odpojení a reconnectu (návrat k opuštěnému AP, nebo restart Mac/iPhone na stejném AP) DHCP selhalo na 5–8 minut. pfSense posílalo DHCPOFFER/ACK korektně, ale packet na konečném AP do wireless sítě nedorazil (100% loss).

Co nepomohlo (Slepé uličky)

  • Vypnutí Hardware Checksum Offload na pfSense (neprokázáno jako příčina).
  • Smazání stale DHCP leases (opravilo to pouze logování duplicate UID, nikoliv packet loss).
  • UniFi Fixy: Vypnutí Fast Roaming, UAPSD, Downgrade U6-LR na starší ovladač 6.6.73 — AP za bug nemohlo, ping selhával i přímo z něj.
  • Proxmox Firewall: Změny fwbr na linux bridge (shoda okolností, že restart sítě dočasně pročistil switch tabulku).

SKUTEČNÁ PŘÍČINA: Inter-Switch Roaming + MAC Aging Time

Zásadní informace byla, že problém začal až po přidání Switche 2, kdy se APčka rozdělila mezi dva fyzické switche spojené LACP Trunkem. Když iPhone (Apple obecně) udělal roam mezi AP na různých switchích, MAC tabulky se zbláznily:

  1. iPhone odejde z AP na Switchi 1 k AP na Switchi 2.
  2. Vráti se zpět k AP na Switchi 1. AP vytvoří linku.
  3. Switch 2 si ale stále pamatuje, že iPhone před minutu odešel od něj směrem k TRK1 (Trunk na Switch 1), a má na tuto MAC adresu tvrdý Aging Time (5 minut).
  4. Když pfSense (či cokoliv zvenčí) pošle paket na iPhone (např. DHCPOFFER) a ten prochází přes Switch 2, Switch 2 vidí: "iPhone je na TRK 1" a pošle paket špatným směrem (pryč od reálného AP1).
  5. Paket oběžnou dráhou umře, dokud nevyprší 5ti minutový Aging Time na Switchi 2, tabulka se smaže a MAC learning se udělá nanovo.

Správné řešení

V SOHO (Home/SMB) sítích bez pokročilých "Stacking" protokolů pro okamžitý meziswitchový FDB sync je roaming nutné držet v rámci jednoho fyzického L2 switche. Akce: Fyzicky přepojit všechny Access Pointy tak, aby vedly výhradně do jednoho switche. Tím se lokální přepínání MAC adresy (z portu např. 14 na port 15) odehraje okamžitě a bez ohledu na restové/přechodové stavy na trunku.


2026-02-17 — Rebalancing VM/CT a Alloy Direct

LXC Migration: debian-services (107) → pmx1

Rozhodnutí: Přesunout LXC 107 z pmx2 na pmx1. Důvod: pmx2 měl 6 CT/VM (~24 GB), pmx1 jen 3 (~5 GB). Po migraci lepší resilience — výpadek jednoho nodu nezabije vše.

Alloy: Nginx → Direct Loki

Rozhodnutí: Alloy posílá logy přímo na 192.168.20.20:3100 místo přes Nginx (loki.marada.name). Důvod: Výpadek Nginx by jinak znamenal ztrátu logů. Loki má port binding na 3100, Nginx zůstává pro manuální API přístup.


2026-02-16 — Proxmox ZFS Migration

Cluster: pve/pve2 → pmx1/pmx2

Rozhodnutí: Přejmenovat nody z pve/pve2 na pmx1/pmx2 při reinstalaci. Důvod: Konzistentní pojmenování, oba nody jsou identické (NiPoGi E3B, Ryzen 5 7430U, 30 GB RAM).

Storage: LVM-thin → ZFS

Rozhodnutí: Při reinstalaci přejít z LVM-thin na ZFS root. Důvod: ZFS replication pro DR, snapshoty, lepší integrace s PBS.

DR: PBS Cold Standby → ZFS Replication

Rozhodnutí: Nahradit starou strategii (noční restore ze záloh na pve2) nativní ZFS replikací každých 15 min. Důvod: RPO 15 min místo 24h, RTO ~5 min místo ~45 min. Bez nutnosti skriptů a cronů.

Distribuce VM/CT: Split across nodes

Rozhodnutí: Nekoncentrovat vše na jeden nod. pfSense na pmx1, HA+PBS na pmx2. Důvod: Pokud spadne jeden nod, druhý stále běží s částí služeb. Kritické VM (pfSense, HA, PBS) mají repliky na druhém nodu.

Dokumentace: Monorepo

Rozhodnutí: Zůstáváme u monorepa — docs/ v ha-config repozitáři. Důvod: Vazba mezi dokumentací a konfigurací je příliš těsná. AI agent vidí kód i docs naráz → lepší kontext. Jednoduchost.

Gitea: Odstraněna

Rozhodnutí: Gitea už neběží v debian-services stacku. Důvod: Přesun na GitHub.


2026-02-15 — Centrální Logování (Loki)

Architektura: Syslog → HA Alloy → Loki

Rozhodnutí: Všechny zdroje posílají syslog (RFC5424) na HA Alloy addon (UDP 5514). Alloy přeposílá do Loki na debian-services přes Nginx reverse proxy. Důvod: HA Alloy je single collector — čte journal (HA core + addony) i syslog (infra zařízení). Loki běží v Dockeru na debian-services vedle Grafany.

Relay pro legacy zařízení

Rozhodnutí: Zařízení podporující pouze BSD syslog (RFC3164) — UniFi APs, Aruba switche, TrueNAS — posílají na rsyslog na debian-services (UDP 514), který relayuje do HA. Důvod: Alloy addon nepodporuje RFC3164 přímo. rsyslog na debianu slouží jako translator.