Konfigurujesz cały głosowy zestaw od zera (satelity, Whisper, Piper, Ollama, rozpoznawanie mówcy)? Zacznij od PRZEWODNIK.md, który prowadzi krok po kroku i odsyła tutaj po szczegóły.
Wbudowany asystent Home Assistant rozpoznaje zdania według szablonów: intencje HA, własne
zdania w custom_sentences i automatyzacje z wyzwalaczem zdaniowym. Jest szybki i
pewny, ale wystarczy powiedzieć coś inaczej („Na którą Ola idzie jutro do szkoły?” zamiast
„O której Ola zaczyna lekcje?”) i słyszymy „Nie rozumiem”. Dopisywanie kolejnych wariantów
zdań nie ma końca.
Assist Router to agent rozmowy, który nakłada się na wbudowanego agenta HA. Wszystko, co HA rozumie, działa bez zmian. Gdy HA nie rozpozna zdania:
Małe modele mylą się, gdy mają jednocześnie wybrać temat, osobę, dzień i wywołać narzędzie. Dlatego podział pracy wygląda tak:
Dzięki temu odpowiedzi nie są zmyślone, a mały model wystarcza do większości zadań. Ten sam podział obowiązuje przy sterowaniu: model wskazuje intencję i parametry z list, kod dopasowuje urządzenie, a wykonuje HA.
Sprawdzone w praktyce: cały zestaw działa na co dzień w domu autora na NVIDIA Jetson AGX Orin 64 GB: Ollama (Gemma 4 26B jako router i model odpowiedzi), faster-whisper i Assist Speaker ID. Działa bardzo dobrze: router odpowiada w ok. 0,7 s, a odpowiedź z danych (router i model odpowiedzi) zajmuje ok. 2 s. 64 GB wspólnej pamięci mieści model razem z Whisperem.
Przykładowe wyniki z NVIDIA Jetson AGX Orin 64 GB. Test obejmował 96 pytań do routera, 34 pytania o prawdziwe dane z HA i 8 streszczeń z Wikipedii:
| Rola | Model | Trafność | Czas |
|---|---|---|---|
| Router | Gemma 4 26B (ten sam model co odpowiedzi, w pamięci raz) | 153/153 (z akcjami i bełkotem) | 0,7 s |
| Router (mniejszy) | Gemma 4 E4B | 92/96 | 0,5 s |
| Model odpowiedzi | Gemma 4 26B (MoE, 4B aktywnych) | 34/34 dane, 8/8 streszczeń | 1,2 s / 5,5 s |
| Rozbiór poleceń sterowania | Gemma 4 26B (control_model: answer) | 67/67 polecenia (z akcjami) | 2,7 s |
| Zgadywanie zdania po bełkocie | Gemma 4 26B | z 10 przykładów: 6 trafnych, 3× „nie wiem” (prośba o powtórzenie), 1 śmiała propozycja | 1,6 s |
| Wersja oszczędna | Gemma 4 E4B we wszystkich rolach | 29/34 dane, 8/8 streszczeń, 26/32 polecenia | 0,8 s / 3,8 s / 1,7 s |
Pełne porównanie kilkunastu modeli (Gemma 4, Bielik, PLLuM, Qwen 3.5, Ministral): trafność i czas w każdej roli, wnioski, typowe błędy i wyniki douczania. Test można powtórzyć na własnym sprzęcie: rozdział 12.
custom_components/assist_router do katalogu
/config/custom_components/. Możesz też dodać repozytorium w HACS jako
repozytorium niestandardowe typu „Integracja”.conversation.assist_router) i
wyłącz „Preferuj obsługę poleceń lokalnie”. Przy włączonej opcji HA
rozpoznaje wbudowane intencje przed Assist Routerem: „Ogłoszenie, …” poszłoby syntezą,
a „Kto to mówi?” i nagrywanie wzorca głosu nie miałyby pierwszeństwa. Assist Router i tak
najpierw przekazuje każde zdanie wbudowanemu agentowi, więc nic się nie traci.router.example.yaml z repozytorium do
/config/assist/router.yaml i dopasuj encje do swojego domu (opis pliku:
rozdział 8).Ustawienia → Urządzenia i usługi → Assist Router → Konfiguruj otwiera menu:
| Pozycja | Co robi |
|---|---|
| Ustawienia | Dwa kroki opisane niżej. |
| Plik routera (router.yaml) | Edycja /config/assist/router.yaml wprost
w HA. Przed zapisem plik jest sprawdzany (zły YAML albo błąd konfiguracji pokazuje komunikat
i nic nie zapisuje), a poprzednia wersja zostaje jako router.yaml.bak. Router
czyta zmiany od następnego pytania, bez restartu. |
| Przykładowe komendy | Podgląd zdań, które działają w tym domu, i ich generowanie (patrz niżej). |
| Nierozpoznane komendy | Tabela ostatnich 30 wpisów dziennika (od najnowszych)
i opcja „Wyczyść dziennik”; poprzednia treść zostaje jako
unrecognized_commands.csv.bak. |
Akcja assist_router.generate_examples albo pozycja menu „Przykładowe
komendy” działa tak:
router.yaml model odpowiedzi układa zdania z
prawdziwymi nazwami: encjami tematu, urządzeniami i pokojami (sterowanie) oraz domownikami.Wynik:
/config/assist/router_examples.yaml: dzialaja to zdania
działające; odrzucone to zdania z informacją, dokąd trafiły, co jest
wskazówką, który opis tematu albo przykład poprawić;topics są zdania tematów;Generowanie trwa kilka minut, bo każde zdanie to osobne zapytanie do routera. Opcje
akcji: per_topic (ile zdań na temat, domyślnie 5) i topics (tylko
wybrane tematy). Temat bełkotu (unclear) jest pomijany.
Akcja assist_router.clear_log czyści dziennik nierozpoznanych. Sensor
„Nierozpoznane komendy” pokazuje liczbę wpisów, a w atrybucie
last ostatnie 20, co przydaje się do karty Markdown na dashboardzie.
Ustawienia, krok pierwszy:
| Opcja | Co robi |
|---|---|
| Zapisuj nierozpoznane komendy | Dopisuje każde nierozpoznane zdanie do
/config/assist/unrecognized_commands.csv i wysyła zdarzenie
assist_router_unrecognized. |
| Agent zapasowy | Agent rozmowy dla pytań, którym router nie dał danych (temat „inne”, błąd). Puste = komunikat „Powiedziałeś: …”. |
| Czas na odpowiedź agenta zapasowego | Po tym czasie słychać „Powiedziałeś: …”. |
| Adres Ollamy | Np. http://localhost:11434. Puste = router wyłączony. |
Krok drugi pojawia się, gdy podasz adres Ollamy. Lista modeli jest pobierana z serwera:
| Opcja | Co robi |
|---|---|
| Model routera | Wybiera temat. Wystarczy mały i szybki model. |
| Model odpowiedzi | Opcjonalny, ale potrzebny do odpowiedzi z danych. Pisze odpowiedź z danych tematu i streszcza Wikipedię. Bez niego pytania z danymi trafiają do agenta zapasowego, a z Wikipedii czytany jest początek artykułu. |
| Czas na odpowiedź routera / modelu | Domyślnie 8 s i 15 s. Ten sam limit routera dotyczy też wyszukiwania w Wikipedii. |
| Trzymaj model w pamięci | Minuty od ostatniego pytania. -1 =
zawsze w pamięci, najszybsza odpowiedź. Oba modele są ładowane przy starcie integracji. |
/config/assist/router.yaml opisuje Twój dom. Kod integracji jest wspólny dla
wszystkich użytkowników. Zmiany w pliku działają od razu, bez restartu HA. Brak pliku
wyłącza router. Poniżej szkielet z najważniejszymi polami; pełny przykład domu z
kilkunastoma tematami jest w rozdziale 8, a to, jak kod zbiera i
przycina dane, opisuje rozdział 7.
# Kto mieszka w domu - trafia do promptu routera i modelu odpowiedzi
household: "Domownicy: Tomek (tata), Ania (mama), dzieci: Ola, Kuba."
# Osoby w pytaniach: match = początki słów (regex), żeby złapać odmianę
people:
ola: {name: Ola, match: "ola|oli|olą|olę|aleksandr"}
kuba: {name: Kuba, match: "kub|jakub"}
tomek: {name: Tomek, match: "tom|tat(a|y|o|ą)\\b"}
# family: {parents: [tomek, ania], children: [ola, kuba], men: [tomek, kuba]}
# speaker: identyfikator głosu w Assist Speaker ID (domyślnie klucz osoby, tu „ola”, „kuba”)
# owns: [calendar.kalendarz_oli] - encje tej osoby; room: Pokój Oli - jej pokój
sentences: 4 # ile zdań w streszczeniu z Wikipedii
wikipedia_language: pl
only_exposed: false # true = encje tematów tylko spośród udostępnionych Assistowi
control_model: answer # kto rozbiera polecenia sterowania: router albo answer
confirm_entities: # zawsze dopytaj (wzorce entity_id albo nazwy)
- switch.kuchnia_lodowka_przelacznik
- switch.*_enabled
music_players: # „puść …”: pokój -> odtwarzacze, pierwszy dostępny
Salon: [media_player.salon_homepod, media_player.salon_home_assistant_voice]
topics:
lekcje:
description: szkoła, plan lekcji, zajęcia dodatkowe, o której kończy lekcje
examples: ["Na którą Ola idzie jutro do szkoły?"]
details: [sensor.plan_lekcji] # atrybuty encji, przycięte do osoby i dnia z pytania
days: dzis # bez dnia w pytaniu - dziś
drop: [teacher, classroom] # pomijane klucze
kalendarz:
description: plany, wydarzenia, spotkania, urodziny
examples: ["Co mamy w weekend?"]
days: "14" # bez dnia w pytaniu - 14 dni od dziś
fetch: # usługa z odpowiedzią
- action: calendar.get_events
entities: [calendar.rodzina, calendar.praca]
data: {start_date_time: "{start}", end_date_time: "{end}"}
merge: events # jedna lista ze wszystkich kalendarzy, po kolei
title: Wydarzenia w kalendarzach
samochod:
description: auto - paliwo, zasięg, przebieg, czy zamknięte, gdzie stoi
examples: ["Ile paliwa ma auto?", "Czy zamknąłem samochód?"]
entities: [sensor.moje_auto_*, binary_sensor.moje_auto_*, lock.moje_auto_*]
swiatla:
description: które światła są włączone
entities: [light.*, "!light.ipad_*"] # „!” = wykluczenie
skip_states: ["off"]
sterowanie:
description: polecenia - włącz, wyłącz, ustaw, przyciemnij, rolety, cieplej, głośniej
examples: ["Zrób jaśniej w salonie.", "Opuść rolety do połowy."]
control: true
wiedza:
description: pytanie o wiedzę ogólną - kim był, co to jest, historia, geografia
examples: ["Kim był Mikołaj Kopernik?"]
search: wikipedia
inne:
description: wszystko inne - rozmowa, żart, polecenia
examples: ["Opowiedz coś ciekawego."]
| Pole | Znaczenie |
|---|---|
description | Krótki opis dla routera: o czym są pytania z tego tematu. Opisy tematów nie powinny się pokrywać. |
examples | 2–4 przykładowe pytania. Najbardziej pomagają przy sformułowaniach z pogranicza dwóch tematów. |
entities | Wzorce entity_id (* = dowolny
ciąg, |klasa1,klasa2 = tylko te rodzaje czujników, !wzorzec =
wykluczenie). Ich stany trafiają do danych po polsku: okno „otwarte”, zamek „zamknięty”.
Pomijane są unknown/unavailable, najwyżej 80 encji, w kolejności
wzorców (ważniejsze pierwsze). |
details | Wzorce encji, których atrybuty idą do
modelu (np. plan lekcji, saldo kont, odjazdy tramwajów). Listy są przycinane do osoby z
pytania (klucz child albo person) i do dnia (klucz
date albo datetime); szum (html, awatary, kolory, wartości
false) jest pomijany, czasy są lokalne i z dniem tygodnia. |
fetch | Usługi z odpowiedzią: action,
entities, data ({start} i {end} = zakres
dni z pytania), opcjonalnie merge (lista do połączenia z wszystkich encji),
title i limit (najwięcej pozycji, domyślnie 40). Np. kalendarze
(calendar.get_events) albo prognoza (weather.get_forecasts). |
days | Dzień, gdy pytanie go nie podaje: dzis,
jutro, tydzien albo liczba dni od dziś ("14"). Bez
tego pola dane nie są filtrowane po dniu. |
drop | Klucze atrybutów pomijane w tym temacie. |
personal | true: temat dla osoby, która pyta, także bez
„mam” i „moje” („Dzień dobry”). Rodzic (family.parents) dostaje swoje dane i dane
dzieci, a dziecko tylko swoje. Mówca nieznany oznacza dane wszystkich. Imię w pytaniu
zawsze wygrywa. |
answer_hint | Dodatkowa instrukcja dla modelu odpowiedzi w tym temacie, np. jak ułożyć podsumowanie dnia i co powiedzieć rodzicowi, a co dziecku. |
verbatim | true: odpowiedź HA na wzorcowe zdanie z command jest
czytana dosłownie, bez modelu odpowiedzi. Kilka zdań oddzielonych ; daje wszystkie
odpowiedzi po kolei. Przydaje się, gdy automatyzacja ma stały układ odpowiedzi, np.
wiadomości dnia: „Dzisiejsze wiadomości z Polski: Pierwsza wiadomość…”. |
answer_hints | Instrukcje zależne od tego, kto pyta. Klucze, po kolei: osoba
(np. ola), girl/boy (dzieci), child/parent,
unknown (mówca nierozpoznany). Brak pasującego klucza oznacza answer_hint.
Prosta instrukcja dla jednej osoby działa pewniej niż jedna długa z warunkami. |
answer_tokens | Dłuższa odpowiedź niż domyślne 160 tokenów, np. 360 dla podsumowania dnia. |
command | (starsze) Szablon HA (Jinja) z wzorcowym pytaniem do
Twojego HA, np. zdaniem automatyzacji. Kilka pytań rozdziel znakiem ;.
Zamiast tego lepiej użyć details i fetch - bez osobnych
automatyzacji Assist. |
search: wikipedia | Temat wiedzy: hasło jest brane z pytania, artykuł streszczany. |
control: true | Temat poleceń sterowania (rozdział 10). |
unclear: true | Temat bełkotu z rozpoznawania mowy: model odgaduje, co powiedziano, a asystent pyta „Czy chodziło o…?”; bez propozycji prosi o powtórzenie (rozdział 10). |
| brak danych i search | Pytanie idzie do agenta zapasowego. |
command (starsze pole)| Zmienna | Wartość |
|---|---|
osoba | pierwsze imię z pytania (forma z people.name) albo brak |
osoby | wszystkie imiona z pytania, po kolei: „Ola i Kuba” → ['Ola', 'Kuba'] |
kto | klucz pierwszej osoby z people (np. ola) |
kiedy | dzis, jutro, pojutrze, weekend, tydzien, dzień tygodnia (poniedzialek … niedziela) albo brak |
tekst | pełne pytanie, np. do wyciągnięcia numeru linii tramwaju |
Domyślne prompty są wbudowane w integrację. Możesz je nadpisać w router.yaml
polami answer_prompt (odpowiedź z danych) i summary_prompt
(streszczenie). W tekście możesz użyć pól {household}, {now} (data i
godzina po polsku) i {sentences}.
Router nie potrzebuje specjalnych automatyzacji ani zdań Assist. Dane bierze wprost z Home
Assistanta, a w router.yaml opisujesz tylko, które encje i
usługi należą do tematu. Resztą zajmuje się kod: wybiera z danych to, o co pytano, usuwa
szum i podaje modelowi odpowiedzi zwięzłą listę faktów.
| Pole tematu | Co trafia do modelu | Typowe przykłady |
|---|---|---|
entities | Stan każdej pasującej encji, po polsku, z najważniejszymi atrybutami: jasność światła, pozycja rolety, temperatura termostatu, co gra, od kiedy czujnik jest otwarty. | okna i drzwi, światła, czujniki w pokojach, auto, osoby
(person.*), ruch, alarm, pralka |
details | Atrybuty encji, czyli dane, których nie widać w samym stanie: listy, plany, salda, adresy. | plan lekcji z dziennika, odjazdy z przystanku,
adres telefonu (*_geocoded_location), konta kieszonkowego |
fetch | Odpowiedź usługi HA, która zwraca dane. Zakres dni wynika z pytania. | kalendarze (calendar.get_events), prognoza
(weather.get_forecasts) |
W jednym temacie możesz łączyć wszystkie trzy. Pogoda w przykładzie bierze bieżący odczyt z
czujników (entities) i prognozę z usługi (fetch).
child albo
person zostaje tylko wtedy, gdy pasuje do osoby z pytania (wzorzec
match z people, więc „Ola” trafia też w „Aleksandra”). Gdy osoby
z pytania w ogóle nie ma w danych (tata pyta o konta dzieci), nic nie jest usuwane.
Pytanie o „dzieci” albo bez imienia daje dane wszystkich.date albo datetime
zostaje tylko wtedy, gdy wypada w dniu z pytania. Gdy pytanie nie podaje dnia, liczy się
days tematu. Bez days dane nie są filtrowane po dniu. Gdy w danym
dniu nic nie ma (lekcje w sobotę), model dostaje „brak” zamiast pustej listy. Filtr nie
dotyczy samego korzenia atrybutów, bo tam date to zwykle data odczytu.friendly_name, wartości false („odwołana: nie”) i puste
wartości oraz klucze z drop.2026-10-24 12:00 (sobota). Model nie
musi liczyć, który dzień to sobota.entities (w kolejności wzorców,
więc najważniejsze wpisz pierwsze), 40 pozycji listy (limit w
fetch zmienia to dla usługi) i 6000 znaków na jedno źródło.| W pytaniu | Wartość | Zakres dni |
|---|---|---|
| „dziś”, „dzisiaj”, „teraz” | dzis | dziś |
| „jutro”, „pojutrze” | jutro, pojutrze | ten jeden dzień |
| „w poniedziałek” … „w niedzielę” | poniedzialek … niedziela | najbliższy taki dzień (dziś, jeśli to dziś) |
| „w weekend” | weekend | najbliższa sobota i niedziela |
| „w tym tygodniu” | tydzien | dziś i 6 kolejnych dni |
| nic | days tematu | np. dzis albo "30" (dziś i 29 kolejnych dni) |
Pytanie: „Na którą Ola idzie jutro do szkoły?”. Router wybiera temat
szkola, a kod wyciąga osobę ola i dzień jutro. Temat ma
details: [sensor.dziennik_plan_lekcji]. Atrybuty tej encji to cały tydzień obojga
dzieci, kilkanaście tysięcy znaków:
{"friendly_name": "Plan lekcji", "week_start": "2026-10-19",
"children": [
{"child": "Aleksandra", "avatar": "https://…", "color": "#f5ec00",
"days": [
{"date": "2026-10-22", "lessons": [{"start": "08:00", "subject": "Matematyka", "canceled": false}, …]},
{"date": "2026-10-23", "lessons": [{"start": "08:00", "subject": "Matematyka", "canceled": false},
{"start": "08:55", "subject": "Przyroda", "canceled": true}, …]},
…]},
{"child": "Jakub", "days": […]}]}
Po przycięciu model dostaje tylko jutro Oli, bez awatarów, kolorów i
canceled: false:
Plan lekcji (stan: 2026-10-22):
week_start: 2026-10-19 (poniedziałek)
children:
- child: Aleksandra
days:
- date: 2026-10-23 (piątek)
lessons:
- start: 08:00
subject: Matematyka
- start: 08:55
subject: Przyroda
canceled: true
Odpowiedź modelu: „Ola idzie jutro na 8:00, pierwsza jest matematyka. Przyroda o 8:55
jest odwołana.” Model sam zauważył odwołaną lekcję, bo flaga true została w
danych.
Bez merge każdy kalendarz byłby osobnym źródłem i model musiałby sam układać
wydarzenia w kolejności. Z merge pytanie „Co mamy w piątek?” dostaje gotowy plan
dnia, a „Kiedy są urodziny babci?” przeszukuje 30 dni (days: "30").
fetch. Integracja wywołuje
tylko usługi, które wyłącznie zwracają dane (w HA: SupportsResponse.ONLY,
np. calendar.get_events, weather.get_forecasts,
todo.get_items). Wpis z usługą, która coś robi (lock.unlock, skrypt),
jest pomijany z ostrzeżeniem w logu. Pytanie nigdy niczego nie przełączy.Nie są potrzebne. Jeśli już je masz, mogą zostać: HA sprawdza je przed routerem,
więc zdanie dokładnie pasujące do automatyzacji dostaje odpowiedź w ułamku sekundy, bez modelu.
Każde inne sformułowanie trafia do routera i odpowiada na nie model z danych. Starsze pole
command (wzorcowe pytanie do automatyzacji) nadal działa, ale nowe tematy lepiej
opisywać przez entities, details i fetch.
Z integracją Speaker ID (zamiana mowy na tekst, która rozpoznaje też głos) router wie, kto zadał pytanie. Wystarczy w pipeline'ach ustawić jej STT („Mowa na tekst z mówcą”). Agentem zostaje Polski Assist, który sam odczytuje mówcę z tego samego przebiegu pipeline'u.
details są przycinane do jego
danych, jakby padło jego imię.people, nie ze Assist Speaker ID.speaker przy osobie.
Domyślnie jest to klucz osoby, więc wystarczy zapisać wzorce głosu pod tymi samymi
identyfikatorami (ola, kuba…).Encje i pokój osoby (owns, room):
owns to wzorce encji należących do osoby, np. jej
kalendarz albo lokalizacja jej telefonu. Gdy pytanie dotyczy konkretnej osoby (z imienia
albo „mam”, „moje” od rozpoznanego mówcy), encje innych osób są pomijane w
details, fetch i entities. Encje niczyje, np.
kalendarz rodzinny, są zawsze brane. „Co mam jutro?” od Oli to więc kalendarz rodzinny
i jej własny, bez kalendarza pracy taty. To jest potrzebne, bo wydarzenia kalendarza
nie mają pola osoby, po którym dałoby się je przyciąć.room to pokój osoby. „Zgaś światło w moim pokoju” albo
„u mnie w pokoju” od tej osoby oznacza ten obszar.family:
parents, children, men). „Mój brat”, „moja
siostra”, „rodzeństwo” od dziecka oraz „mój syn”, „moja córka”, „moje dzieci” od
rodzica wskazują odpowiednie osoby, np. „moja córka” od taty to wszystkie córki.
Dopisują się do imion z pytania („Czy mama i mój brat są w domu?”).
„Mama”, „tata”, „żona” i „mąż” zostają stałymi imionami w match.mowca() w
custom_templates.Podsumowanie dnia i „dobranoc” dla osoby: tematy z
personal: true zbierają dane mówcy. „Dzień dobry”, „Jak wygląda mój dzień?”, „Co
mnie dziś czeka?” od dziecka dają jego lekcje, sprawdziany, obowiązki i kalendarz. Od rodzica
dają jego kalendarz i pogodę oraz krótki skrót o dzieciach. Jak ułożyć odpowiedź, mówi
answer_hint tematu. Przykład: tematy podsumowanie_dnia i
dobranoc w router.example.yaml. Sceny o tych nazwach uruchamia się
tylko jawnie („Uruchom scenę Dzień dobry”). Samo „Dzień dobry” jest powitaniem.
Ogłoszenia (intercom): zdanie zaczynające się od „ogłoszenie”,
„komunikat”, „do wszystkich” albo „intercom” („Ogłoszenie, dzieci proszę przyjść na
obiad”) nie trafia do HA ani do modeli. Assist Speaker ID odtwarza nagranie mówcy,
bez cięcia i bez zmian, na pozostałych głośnikach, a głośnik, który nagrał, mówi tylko
„Ogłoszenie wysłane.”. Odtwarzacze dodatkowe, np. HomePody, podajesz w
router.yaml jako announce_players: [media_player.…]. Bez nagrania
(tekst wpisany ręcznie albo brak Assist Speaker ID) zdanie idzie dalej zwykłą drogą: HA czyta je
syntezą mowy. Te słowa muszą być na początku zdania, więc „Jakie są ogłoszenia w Librusie?”
to zwykłe pytanie.
Sprawdzenie rozpoznawania: „Kto to mówi?”, „Kim jestem?”, „Jak mnie rozpoznajesz?”, „Co to za głos?”. Asystent odpowiada od razu, bez HA i modeli, z wyniku tej samej wypowiedzi, np. „Mówi Ola, podobieństwo 82 procent. Następna osoba: Kuba, 35 procent.” Odpowiedź może też brzmieć „nie rozpoznaję tego głosu” albo „nie jestem pewien”, z dwoma najbardziej podobnymi głosami. Krótkie pytanie (poniżej 1 s mowy) daje prośbę o dłuższe zdanie, np. „Powiedz mi, kto teraz do ciebie mówi”.
Plik router.example.yaml w repozytorium opisuje zmyślony dom: Tomek, Ania i
dwoje dzieci, Ola i Kuba. Encje mają nazwy, jakie zwykle tworzą popularne integracje:
aplikacja mobilna HA (lokalizacja telefonów), Frigate (kamery), Met.no (pogoda), kalendarze,
auto, odkurzacz, pralka i listy zakupów. Wystarczy podmienić nazwy encji na swoje
(Narzędzia deweloperskie → Stany).
| Temat | Skąd dane | Co pokazuje |
|---|---|---|
kalendarz | fetch: calendar.get_events, merge | kilka kalendarzy w jednej liście, dzień z pytania, bez dnia 30 dni |
szkola | details albo fetch | plan lekcji przycięty do dziecka i dnia; dwa warianty dziennika |
pogoda | entities + 2 × fetch | odczyt z czujnika i prognoza dzienna oraz godzinowa (limit: 24) |
czujniki | entities z |klasa | czujniki według device_class, tylko udostępnione |
okna_drzwi | entities | okna, drzwi, garaż i rolety z czasem „od kiedy” |
osoby | entities + details | domownicy, kamery Frigate, ruch, adres z telefonu; kolejność wzorców |
samochod | entities | wszystkie czujniki auta - model wybiera |
swiatla | entities, skip_states, ! | tylko świecące, bez ekranu tabletu |
media | entities, skip_states | tylko odtwarzacze, które coś grają |
listy | entities: [todo.*] | pozycje list (integracja sama woła todo.get_items) |
komunikacja | details | odjazdy z przystanków z atrybutów |
niezrozumiale | unclear: true | bełkot z rozpoznawania mowy: propozycja „Czy chodziło o…?” albo prośba o powtórzenie |
sterowanie, wiedza, inne | - | polecenia, Wikipedia, agent zapasowy |
# Przykładowy router.yaml dla Assist Routera - skopiuj do /config/assist/router.yaml
# i dopasuj encje do swojego domu (Narzędzia deweloperskie → Stany).
#
# Dom z przykładu: Tomek (tata), Ania (mama), dzieci Ola i Kuba. Encje mają nazwy, jakie
# zwykle tworzą popularne integracje: aplikacja mobilna HA (lokalizacja telefonów),
# Frigate (kamery), Met.no (pogoda), kalendarze Google/CalDAV, integracja auta, odkurzacza
# i pralki. Wszystkie nazwy encji są przykładowe.
#
# Jak router używa tego pliku:
# 1. Model routera wybiera JEDEN temat z `topics` (na podstawie description i examples).
# 2. Kod wyciąga z pytania osoby (`people`) i dzień („jutro”, „w piątek”).
# 3. Temat zbiera dane: stany encji (entities), atrybuty encji (details) i odpowiedzi
# usług (fetch) - przycięte do osoby i dnia z pytania.
# 4. Model odpowiedzi odpowiada na pytanie wyłącznie z tych danych.
# Zmiany w pliku działają od razu, bez restartu HA.
# --- Ustawienia ogólne ---------------------------------------------------------------
# Kto mieszka w domu - trafia do promptów (model wie, że „mama” to Ania, a „dzieci” to
# Ola i Kuba).
household: >-
Domownicy: Tomek (tata, najczęściej pyta), Ania (mama), dzieci: Ola (Aleksandra, 10 lat)
i Kuba (Jakub, 7 lat).
# Osoby w pytaniach. `match` to początki słów (regex), żeby złapać odmianę: „Oli”, „Olą”,
# „Kubie”, „taty”. Osoba z pytania przycina dane, które mają klucz `child` albo `person`
# (np. plan lekcji tylko Oli). Pytanie bez imienia („dzieci”) - dane wszystkich.
# Z rozpoznawaniem mówcy (integracja Assist Speaker ID) pytanie w 1. osobie („Co mam jutro?”)
# dotyczy tego, kto mówi. Dodatkowe pola osoby:
# speaker - identyfikator jej głosu w Assist Speaker ID (domyślnie klucz osoby, np. „ola”),
# owns - jej encje (np. kalendarz pracy, telefon): w pytaniu o inną osobę są pomijane,
# encje niczyje (kalendarz rodziny) - zawsze,
# room - jej pokój: „zgaś światło w moim pokoju” od tej osoby = ten obszar.
people:
tomek:
name: Tomek
match: "tom|tat(a|y|o|ą|ę)\\b"
owns: [calendar.praca_tomka, sensor.telefon_tomka_*]
ania:
name: Ania
match: "ani|anna|anny|mam(a|y|ie|ą|ę)\\b"
owns: [sensor.telefon_ani_*]
ola:
name: Ola
match: "ol(a|i|ą|ę|u)\\b|olk|aleksandr"
room: Pokój Oli
kuba:
name: Kuba
match: "kub|jakub"
room: Pokój Kuby
# Określenia rodzinne względem mówcy: „mój brat” od Oli = Kuba, „moja córka” od Tomka =
# Ola, „moje dzieci” od Ani = Ola i Kuba. „mama”/„tata” to stałe imiona (match wyżej).
family:
parents: [tomek, ania]
children: [ola, kuba]
men: [tomek, kuba]
sentences: 4 # ile zdań w streszczeniu z Wikipedii
wikipedia_language: pl
only_exposed: false # true = encje tematów tylko spośród udostępnionych Assistowi
control_model: answer # rozbiór poleceń sterowania: router albo answer (większy model)
# Sterowanie: przy tych urządzeniach asystent zawsze pyta „Czy wyłączyć…?” - także gdy
# trafią do polecenia zbiorczego („wyłącz wszystkie przełączniki”).
confirm_entities:
- switch.lodowka
- switch.serwer_*
- switch.*_kamera_enabled # włączniki kamer (Frigate)
- Router Wi-Fi # można też podać nazwę urządzenia
# „Puść …”: pokój -> odtwarzacze w kolejności (pierwszy dostępny). Odtwarzacze Music
# Assistanta często nie mają pokoju - stąd ta lista. Bez pokoju w poleceniu: pokój
# głośnika, do którego mówisz.
music_players:
Salon: [media_player.salon_glosnik, media_player.salon_telewizor]
Sypialnia: [media_player.sypialnia_glosnik]
Kuchnia: [media_player.kuchnia_glosnik]
# --- Tematy ---------------------------------------------------------------------------
#
# Źródła danych tematu (można łączyć):
# entities - wzorce entity_id; do modelu idzie stan po polsku („otwarte”, „w domu”).
# `*` = dowolny ciąg, `|klasa1,klasa2` = tylko te device_class,
# `!wzorzec` = wykluczenie. Kolejność ma znaczenie (limit 80 encji).
# details - wzorce encji, których ATRYBUTY idą do modelu (listy, plany, salda).
# fetch - usługi, które zwracają dane (calendar.get_events, weather.get_forecasts…).
# `{start}` i `{end}` = zakres dni z pytania.
# days - dzień, gdy pytanie go nie podaje: dzis, jutro, tydzien albo liczba dni.
# drop - klucze atrybutów do pominięcia (szum, którego model nie potrzebuje).
# skip_states - stany pomijane (np. wyłączone światła - zostają tylko świecące).
topics:
# Podsumowania dla osoby, która pyta (wymaga Assist Speaker ID; bez niego - dla całej rodziny).
# personal: bez imienia w pytaniu dane mówcy, a rodzica także dzieci.
podsumowanie_dnia:
description: dzień dobry, podsumowanie dnia, jak wygląda mój dzień, co mnie dziś czeka, plan na resztę dnia
examples: ["Dzień dobry", "Jak wygląda mój dzisiejszy dzień?", "Co mnie dzisiaj czeka?"]
personal: true
days: dzis
answer_tokens: 360
answer_hint: >-
To podsumowanie dnia na głos. Mów do osoby z „Pyta:”, po imieniu, zwykłymi zdaniami.
Najpierw pogoda, potem jej plan (lekcje, wydarzenia, obowiązki). Rodzicowi dodaj
jedno lub dwa zdania o najważniejszych sprawach dzieci. Do 6 zdań.
details: [sensor.plan_lekcji, sensor.obowiazki]
fetch:
- action: calendar.get_events
entities: [calendar.rodzina, calendar.praca_tomka]
data: {start_date_time: "{start}", end_date_time: "{end}"}
merge: events
- action: weather.get_forecasts
entities: [weather.dom]
data: {type: daily}
dobranoc:
description: dobranoc, idę spać, co mnie czeka jutro
examples: ["Dobranoc", "Idę spać", "Co mnie jutro czeka?"]
personal: true
days: jutro
# instrukcja zależna od mówcy: osoba, girl/boy, child/parent, unknown
answer_hints:
girl: >-
Jednym-dwoma zdaniami powiedz, co ją czeka jutro (bez domu i pogody), potem czule,
bajkowo życz dobrej nocy po imieniu, jak księżniczce.
boy: >-
Jednym-dwoma zdaniami powiedz, co go czeka jutro (bez domu i pogody), potem na wesoło
życz dobrej nocy po imieniu (np. ładowanie baterii superbohatera).
parent: >-
Co go czeka jutro i pogoda rano, co w domu zostało otwarte lub włączone, jedno
zdanie o jutrze dzieci. Zakończ „Dobranoc”. Do 5 zdań.
unknown: >-
Krótko dla całej rodziny, bez imion: co jutro i co otwarte lub włączone w domu.
details: [sensor.plan_lekcji]
fetch:
- action: calendar.get_events
entities: [calendar.rodzina, calendar.praca_tomka]
data: {start_date_time: "{start}", end_date_time: "{end}"}
merge: events
entities: [binary_sensor.*_okno, light.*]
# Kalendarze: wydarzenia ze wszystkich kalendarzy w jednej liście, po kolei.
# „Co mamy w piątek?” -> tylko piątek; „Kiedy są urodziny babci?” -> 30 dni od dziś.
kalendarz:
description: plany, wydarzenia, spotkania, urodziny, imieniny, święta, co mamy zaplanowane, terminy
examples:
- Co mamy w weekend?
- Ile mam jutro spotkań w pracy?
- Kiedy są urodziny babci?
- Czy w czwartek coś się dzieje?
days: "30"
drop: [description] # opisy spotkań (linki do wideorozmów) tylko zajmują miejsce
fetch:
- action: calendar.get_events
entities: [calendar.rodzina, calendar.praca_tomka, calendar.urodziny, calendar.swieta_w_polsce]
data: {start_date_time: "{start}", end_date_time: "{end}"}
merge: events # jedna lista ze wszystkich kalendarzy, posortowana po godzinie
title: Wydarzenia w kalendarzach (po kolei)
limit: 60
# Szkoła z dziennika elektronicznego. Dwa typowe warianty - wybierz pasujący:
# a) integracja daje encję z atrybutami (dzieci -> dni -> lekcje): `details`;
# b) integracja daje kalendarz lekcji dla każdego dziecka: `fetch` jak w kalendarzu.
szkola:
description: szkoła, plan lekcji, o której zaczyna lub kończy lekcje, kiedy wraca ze szkoły, zastępstwa, odwołane lekcje
examples:
- Na którą Ola idzie jutro do szkoły?
- Czy Kuba ma jutro WF?
- O której dzieci kończą dziś lekcje?
details: [sensor.dziennik_plan_lekcji] # wariant a)
days: dzis # bez dnia w pytaniu - dziś
drop: [teacher_id, classroom_id]
# fetch: # wariant b)
# - action: calendar.get_events
# entities: [calendar.lekcje_ola, calendar.lekcje_kuba]
# data: {start_date_time: "{start}", end_date_time: "{end}"}
# merge: events
# Pogoda: teraz z czujników na balkonie, prognoza z usługi (dzienna i godzinowa).
pogoda:
description: pogoda na zewnątrz teraz i prognoza, deszcz, wiatr, czy wziąć parasol, jak się ubrać, temperatura na dworze
examples:
- Czy jutro będzie padać?
- Czy wziąć parasol?
- Jak się ubrać na weekend?
- Ile jest stopni na dworze?
entities:
- sensor.balkon_temperatura
- sensor.balkon_wilgotnosc
- sensor.deszczomierz_*
drop: [wind_bearing, uv_index, cloud_coverage, dew_point, pressure]
fetch:
- action: weather.get_forecasts
entities: [weather.dom]
data: {type: daily}
title: Prognoza na kolejne dni
- action: weather.get_forecasts
entities: [weather.dom]
data: {type: hourly}
title: Prognoza godzinowa
limit: 24 # najwyżej doba - więcej tylko spowalnia odpowiedź
# Klimat w pokojach: czujniki według rodzaju (device_class) i termostaty.
czujniki:
description: temperatura, wilgotność, CO2, jakość powietrza w pokojach, ogrzewanie, termostaty, czy jest duszno
examples:
- Ile stopni jest w pokoju Oli?
- Gdzie jest najwyższe CO2?
- Na ile jest ustawione ogrzewanie w salonie?
only_exposed: true # tylko czujniki udostępnione Assistowi (bez technicznych)
entities:
- sensor.*|temperature,humidity,carbon_dioxide,pm25,volatile_organic_compounds
- climate.*
# Okna, drzwi i rolety: czujniki otwarcia i pozycja rolet.
okna_drzwi:
description: które okna lub drzwi są otwarte, czy wszystko zamknięte, drzwi wejściowe, balkon, garaż, rolety
examples:
- Które okna są otwarte?
- Czy zamknąłem drzwi balkonowe?
- Od kiedy jest otwarte okno w sypialni?
entities:
- binary_sensor.*|door,window,opening,garage_door
- cover.*
# Kto jest w domu i gdzie: domownicy (person), adres z telefonu (aplikacja mobilna HA),
# kamery Frigate (liczba osób), czujniki ruchu. Kolejność: najważniejsze najpierw.
osoby:
description: kto jest w domu, gdzie ktoś jest, czy ktoś jest w pokoju, gdzie był ruch, ile osób widzą kamery
examples:
- Gdzie jest mama?
- Czy Kuba jest już w domu?
- Czy ktoś jest w salonie?
- Ile osób widzi kamera w ogrodzie?
entities:
- person.*
- sensor.*_person_count # Frigate: liczba osób w kadrze
- binary_sensor.*|motion,occupancy,presence
details: [sensor.telefon_*_geocoded_location] # adres telefonu (atrybuty: ulica, miasto)
drop: [Location, Country, ISO Country Code, Postal Code, Time Zone, Areas Of Interest,
Administrative Area, Sub Administrative Area, Ocean, Inland Water]
# Samochód: wszystkie czujniki auta - model wybierze, o co pytano.
samochod:
description: samochód - paliwo, zasięg, przebieg, spalanie, serwis, czy zamknięty, okna, opony, gdzie stoi
examples:
- Czy muszę zatankować?
- Czy zamknąłem auto?
- Gdzie zaparkowałem?
- Kiedy przegląd?
entities:
- sensor.moje_auto_*
- binary_sensor.moje_auto_*
- lock.moje_auto_*
- device_tracker.moje_auto_*
# Światła: tylko świecące (skip_states), bez ekranu tabletu i podświetleń głośników.
swiatla:
description: które światła są włączone, gdzie się świeci, czy zostało zapalone światło, jasność lamp
examples:
- Czy zostawiłem gdzieś zapalone światło?
- Które lampy świecą w kuchni?
only_exposed: true
entities:
- light.*
- "!light.*_ekran"
- "!light.*_led_ring"
skip_states: ["off"]
# Co gra: tylko odtwarzacze, które coś robią.
media:
description: co teraz gra, muzyka, telewizor, głośniki, jaka głośność, czy telewizor jest włączony
examples:
- Co teraz gra w salonie?
- Czy telewizor jest włączony?
only_exposed: true
entities:
- media_player.*
skip_states: ["off", "idle", "standby"]
# Odkurzacz, pralka - stan i czas do końca.
odkurzacz:
description: odkurzacz, robot sprzątający, czy skończył, bateria, filtr, szczotki
examples: ["Czy odkurzacz skończył?", "Ile baterii ma robot?"]
entities: [vacuum.*, sensor.odkurzacz_*]
pranie:
description: pralka, suszarka, czy pranie się skończyło, ile zostało do końca
examples: ["Czy pralka skończyła?", "Ile jeszcze będzie suszyć?"]
entities: [sensor.pralka_*, sensor.suszarka_*]
# Listy (todo): zawartość list, nie tylko liczba pozycji (todo.get_items).
listy:
description: lista zakupów, co trzeba kupić, przypomnienia, rzeczy do zrobienia
examples: ["Co jest na liście zakupów?", "Czy mamy na liście mleko?"]
entities: [todo.*]
# Alarm i bezpieczeństwo - tylko odczyt (włączanie alarmu zostaw automatyzacjom).
alarm:
description: alarm, czy alarm jest włączony, tryb ochrony, zalanie, dym, gaz, czy gdzieś cieknie woda
examples: ["Czy alarm jest włączony?", "Czy czujnik dymu coś wykrył?"]
entities:
- alarm_control_panel.*
- binary_sensor.*|moisture,smoke,gas,carbon_monoxide
# Komunikacja miejska: odjazdy z przystanków (integracje MPK/ZTM mają listę odjazdów
# w atrybutach). Bez dnia - wszystko, co jest w danych.
komunikacja:
description: tramwaj, autobus, odjazdy z przystanku, za ile jest tramwaj
examples: ["Za ile mam tramwaj 8?", "Kiedy jedzie autobus z Parkowej?"]
details: [sensor.przystanek_*]
drop: [html, original_departure]
# Stan pozostałych urządzeń (gniazdka, przełączniki).
urzadzenia:
description: czy urządzenie jest włączone - gniazdka, przełączniki, ładowarki, akwarium
examples: ["Czy akwarium jest włączone?", "Czy ładowarka w garażu działa?"]
only_exposed: true
entities: [switch.*]
# Polecenia, których HA nie rozpoznał: rozbiór na intencję HA i wykonanie
# (z dopytaniem przy wątpliwościach). Szczegóły: readme, rozdział „Sterowanie domem”.
sterowanie:
description: polecenia - włącz, wyłącz, zgaś, zapal, ustaw, przyciemnij, kolor światła, rolety, cieplej, głośniej, puść muzykę, odkurz, dopisz do listy
examples:
- Zrób ciepłe światło w sypialni.
- Opuść rolety do połowy.
- Puść jazz w kuchni.
- Dopisz mleko do listy zakupów.
control: true
# Wiedza ogólna: streszczenie artykułu z Wikipedii (3–4 zdania).
wiedza:
description: pytanie o wiedzę ogólną - kim był, co to jest, historia, geografia, nauka
examples: ["Kim był Mikołaj Kopernik?", "Co to jest fotosynteza?"]
search: wikipedia
# Bełkot z rozpoznawania mowy: słowa do siebie nie pasują. Asystent zgaduje, co powiedziano
# („Czy chodziło o: włącz nastrój w salonie?”) albo prosi o powtórzenie. W opisie napisz
# wprost, co tu NIE należy - inaczej router wrzuci tu też dziwne, ale sensowne pytania.
niezrozumiale:
description: zdanie bez sensu - słowa do siebie nie pasują i nie da się z nich zrozumieć ani pytania, ani polecenia; wygląda na przekręcone przez rozpoznawanie mowy. Tu NIE należą pytania i polecenia z jednym przekręconym słowem, gdy sens jest jasny, ani pytania o wiedzę, rozmowa i żarty
examples:
- Głup nastrój w pisarowie.
- Szuflada wieczór gazeta kuchni.
unclear: true
# Wszystko inne - agent zapasowy (rozmowa, żarty).
inne:
description: wszystko inne - rozmowa, żart, prośby, pytania bez danych w domu
examples: ["Opowiedz coś ciekawego.", "Jak się masz?"]
Przykład: pytania o pralkę i suszarkę, np. „Ile jeszcze będzie prać?” albo „Czy mogę wyjąć pranie?”.
pralka. Zwróć uwagę, czy potrzebna informacja jest w stanie (np.
„czas do końca: 25 min”) - wtedy wystarczy entities, czy w
atrybutach (listy, plany) - wtedy details.fetch; zamiast dat wpisz {start} i
{end}. pranie:
description: pralka, suszarka, czy pranie się skończyło, ile jeszcze potrwa
examples: ["Czy suszarka skończyła?", "Ile jeszcze będzie prać?"]
entities: [sensor.pralka_*, sensor.suszarka_*]route,
zobaczysz np. pranie + 12 encji + odpowiedź, a dla details i
fetch szkola + 1 źródeł + odpowiedź.description i dodaj
pytanie z dziennika do examples.drop, days, limit), brakuje w nich
odpowiedzi (dodaj encję) albo zawierają podobne, mylące encje (wyklucz je
!wzorzec). Pomaga tools/compare_sources.py (rozdział 12).days, żeby pytanie bez dnia nie ściągało całego
tygodnia.Temat z control: true obsługuje polecenia, których HA nie rozpoznał. Zdania hassil
i automatyzacje zawsze mają pierwszeństwo.
WHISPER_FIXES w control.py.intencja to intencja HA: HassTurnOn/Off,
HassLightSet, HassSetPosition,
HassClimateSetTemperature, HassSetVolume/Relative,
sterowanie mediami, HassMediaSearchAndPlay („puść …”), odkurzacz lub
HassListAddItem. Pola
domena, obszar i pietro to wybór z list Twojego
HA. Do tego dochodzą nazwa (fragment wypowiedzi), wszystkie,
wartosc i zmiana (więcej / mniej), a dla światła
kolor (np. czerwony) i barwa (ciepła 2700 K, neutralna 4000 K,
zimna 6500 K), dla muzyki szukaj i rodzaj (utwór, album,
wykonawca, playlista, gatunek, podcast).confirm_entities (także w poleceniu
zbiorczym, np. „wyłącz wszystkie przełączniki”).HassTurnOn dla zamka = zarygluj). Inna wypowiedź unieważnia pytanie.intent.async_handle (HA):
DUPLICATE_NAME) i nic nie robi, a nie da się w niej wskazać
entity_id. Integracja nie pyta więc o „kilka pasujących urządzeń”, tylko
wywołuje usługę na każdej z tych encji (media_player.volume_set,
homeassistant.turn_on, light.turn_on …); pauza i następny
utwór - tylko na tej, która gra. Nazwa podana przez model razem z pokojem
(„Łazienka HomePod”) jest porównywana bez słowa pokoju.confirm_entities.unclear: true łapie zdania, w których słowa do siebie nie pasują. Model
odgaduje, co powiedziano - zna nazwy pomieszczeń i akcji - a asystent pyta: „Nie jestem
pewien, czy dobrze usłyszałem. Czy chodziło o: włącz nastrój w salonie?”. Na „tak”
poprawione zdanie przechodzi całą zwykłą drogę (zdania HA, automatyzacje, router); na
„nie” asystent prosi o powtórzenie, „anuluj” kończy. Gdy nie da się zgadnąć - od razu
„Nie zrozumiałem. Powtórz, proszę.” W opisie tematu warto napisać wprost, co tu
nie należy: pytania i polecenia z jednym przekręconym słowem, gdy sens jest jasny,
pytania o wiedzę, rozmowa. Nic nie jest wykonywane bez potwierdzenia. Asystent prosi o powtórzenie najwyżej raz z rzędu. Gdy drugi raz nie zrozumie (znów bełkot albo znów „nie” na jego propozycję), mówi „Przepraszam, nie rozumiem. Jeśli będziesz czegoś potrzebować, poproś mnie jeszcze raz.” i kończy rozmowę - mikrofon się zamyka. Każde zrozumiane polecenie albo pytanie zeruje licznik; liczy się każda rozmowa (głośnik) osobno.media_player.search_media,
np. w Music Assistancie) i odtworzenie pierwszego wyniku. Odtwarzacz wybiera kod:
z music_players (pierwszy dostępny), bez wpisu odtwarzacz z wyszukiwaniem w
tym obszarze albo z nazwą pokoju w nazwie, a bez pokoju w poleceniu pokój satelity.
Światło bez obsługi koloru lub barwy dostaje komunikat zamiast błędu.confirm_entities, a tych, których nie chcesz przełączać głosem wcale, nie
udostępniaj./config/assist/unrecognized_commands.csv (UTF-8, separator ;)
otwiera się w Numbers i Excelu. Kolumny:
| Kolumna | Treść |
|---|---|
timestamp, area, device | kiedy i na którym głośniku |
kind | no_intent_match (HA nic nie rozpoznało) albo
no_valid_targets (rozpoznało polecenie dla nieistniejącego obszaru lub
urządzenia, np. „…w szkole”) |
text | co usłyszał Whisper |
response | co odpowiedział asystent |
route | droga routera: temat, liczba encji i źródeł (details, fetch), „+ odpowiedź” / „+ streszczenie”, a przy sterowaniu intencja i parametry |
speaker | rozpoznany mówca (Assist Speaker ID), puste = nieznany |
Przeglądaj go co jakiś czas. Pytania z pustym route albo złą odpowiedzią
pokazują, który temat poprawić. Częste zdania warto dopisać jako zwykłe zdania HA, bo te
działają natychmiast.
sensor.assist_router_nierozpoznane_komendy podaje
liczbę wpisów, a w atrybucie last ostatnie 20. Przydaje się do karty
Markdown na dashboardzie.assist_router.clear_log albo
„Wyczyść dziennik” w opcjach. Nagłówek zostaje, a poprzednia treść ląduje w
unrecognized_commands.csv.bak.speaker zawiera rozpoznanego mówcę (Assist Speaker ID).W katalogu tools/ jest pięć narzędzi (Python, uruchamiane z dowolnego
komputera z dostępem do Ollamy):
snapshot_data.py zapisuje migawkę prawdziwych danych: odpowiedzi HA na
wzorcowe pytania i artykuły z Wikipedii. Oczekiwane fakty w teście się wtedy nie zmieniają.model_benchmark.py testuje modele po kolei:
control sprawdza rozbiór poleceń na obrazie domu z migawki, niczego nie
wykonując. Odtwarza całą decyzję integracji: zabezpieczenie alarmu, samą odpowiedź
„tak/nie”, nazwę skryptu bez modelu i rozbiór z listą akcji (skrypty z opisami, sceny).
Sprawdza intencję, obszar, dopasowane urządzenie lub akcję i to, czy asystent by dopytał.
snapshot_data.py --only-home odświeża sam obraz domu, np. po zmianie opisów
albo aliasów. Same testy kodu (pytest) sprawdzają sterowanie od zdania do
wywołanej usługi na sztucznym domu (tests/test_actions.py). Opcja
--rescore ocenia zapisane odpowiedzi ponownie po poprawkach testu.model_report.py tworzy raport HTML i dopisuje historię do CSV, więc nowe
modele można porównać ze starymi.compare_sources.py to test zgodności przy zmianie źródła danych tematu.
Dla każdego pytania z listy liczy dane po staremu i po nowemu (dwa pliki router.yaml), np.
zdania automatyzacji kontra details/fetch, na żywych danych z HA.
Odpowiedzi modelu zapisuje obok siebie. Tylko odczyt.check_sentences.py sprawdza Twoje automatyzacje ze zdaniami. Z każdego wzorca
wyzwalacza conversation generuje kilka zdań i pyta HA (tryb debug, nic nie
jest wykonywane), co by je obsłużyło. Zgłasza zdania, które trafiają w inną automatyzację
albo intencję HA, nie pasują do niczego, oraz automatyzacje wyłączone. Uruchamiaj po
każdej zmianie zdań..venv/bin/python tools/model_benchmark.py --ollama http://localhost:11434 \
--router-config /config/assist/router.yaml --cases model_test.yaml \
--data model_test_data.yaml --router gemma4:e4b --answer gemma4:26b --pull --out wyniki.json
.venv/bin/python tools/model_report.py wyniki.json --label "test" \
--html result.html --csv model_results.csv --answers-csv model_answers.csv
.venv/bin/python tools/compare_sources.py --ha http://homeassistant.local:8123 \
--token-file ~/.ha_token --ollama http://localhost:11434 \
--old router.yaml --new router.new.yaml --questions pytania.txt --out porownanie.json
.venv/bin/python tools/check_sentences.py --ha http://homeassistant.local:8123 \
--token-file ~/.ha_token --config /config --per-template 5
Plik przypadków testowych (YAML) zawiera pytania z oczekiwanym tematem oraz dane z pytaniem i
oczekiwanymi faktami. Format opisuje README.md. Trzymaj go poza repozytorium, bo
zawiera dane Twojego domu.
Ogólny mały model rozpoznaje tematy dobrze, ale można go douczyć na danych własnego domu. Służy do tego osobny zestaw skryptów, które w przyszłości trafią do osobnego repozytorium. Do nauki potrzebna jest karta graficzna (np. RTX 4090), a model po douczeniu działa w Ollamie jak każdy inny. Kroki:
router.yaml;Po zmianie listy tematów douczony model trzeba douczyć ponownie.
Doświadczenie z pierwszej próby: Gemma 4 E2B douczona na samych zdaniach z szablonów rozpoznawała je w 99%, ale na swobodnych pytaniach z dziennika wypadła gorzej od zwykłej Gemmy 4 E4B (88/96 wobec 93/96). Nauczyła się stylu szablonów, a nie języka. Dlatego drugi przebieg łączy szablony z parafrazami, które pisze duży model (Gemma 4 26B): naturalne pytania z imionami, pokojami i błędami Whispera. Bazą jest E4B.
router.yaml, przycięte do osoby i dnia. Z
only_exposed: true są to wyłącznie encje udostępnione Assistowi.fetch wywołuje tylko
usługi, które wyłącznie zwracają dane. Sterowanie przechodzi wyłącznie przez temat
control: true i intencje HA, które sprawdzają udostępnienie encji. Niepewne
polecenia, zamki, bramy i urządzenia z confirm_entities wymagają
potwierdzenia.