Historia zmian#
vCLU zapisuje każdą zmianę stanu w domu: co się zmieniło, z jakiej wartości na jaką, kiedy i kto to zrobił. Dzięki temu pytanie „czemu światło zapaliło się o 3 w nocy?” ma odpowiedź, a automatyka może odróżnić swoje działanie od ręcznego.
Co jest zapisywane#
Każdy wpis to jedna zmiana:
| Pole | Opis |
|---|---|
path | Obiekt, np. CLU220000592.DOU4992 albo satel.zone.2 |
old, value | Wartość przed i po zmianie |
atMs | Czas zmiany (Unix, milisekundy) |
origin | Którędy przyszła zmiana: grenton, physical (np. Satel), web, mcp, mqtt, homekit, api, lua, system |
actor | Kto ją zrobił (tabela niżej) |
correlationId | Wspólny identyfikator zmian wywołanych jedną akcją, np. sceną |
Jedna prawdziwa zmiana daje jeden wpis, a ustawienie tej samej wartości nie jest zmianą. Po starcie albo reloadzie pierwszy odczyt równy ostatniej znanej wartości też nie jest zmianą. Jeśli wartość jest inna (np. ktoś przełączył światło, gdy vCLU nie działało), zapisuje się raz jako zmiana z origin = system i sprawcą system:startup.
Kto to zrobił: actor#
| Źródło zmiany | actor |
|---|---|
| Raport z CLU (włącznik, wejście, logika CLU) | device:<CLU> |
| Skrypt wysłany przez CLU do vCLU | device:<nazwa CLU> |
| Satel (czujka, klawiatura) | device:satel |
| Wejście GPIO | device:gpio |
| Panel vCLU (jedno konto administratora) | user:<login>, user:local przy wyłączonym logowaniu |
| Dashboard otwarty publicznie lub przez PIN, bez logowania | dashboard:<id> (z zalogowanej przeglądarki: user:<login>) |
| Agent AI przez MCP | agent:<id bota> |
| Klucz API | service:web-api |
| Endpoint HTTP napisany w Lua | service:http |
| Home Assistant | service:home-assistant |
| Inne komendy MQTT | service:mqtt |
| HomeKit | service:homekit |
Automatyka z runAs | automation:<id> |
| Kod uruchamiany przy ładowaniu modułu (i jego timery) | automation:module:<plik> |
Handler bez runAs | sprawca zdarzenia, które go wywołało (np. agent:…), w innym wypadku automation:unknown |
| Kod uruchamiany przy ładowaniu pluginu | plugin:<id> |
| Zmiana wykryta po starcie | system:startup |
Polecenie switchOn(ms) na wyjściu Grentona przypisuje także samoczynne wyłączenie przez CLU temu, kto je wydał. Automatyka, która zapaliła światło na minutę, rozpozna więc zgaszenie po tej minucie jako swoje, a nie jako ręczne.
Gdzie leży#
- W pamięci: domyślnie ostatnie 24 h, do 10 000 zmian na obiekt i 50 000 łącznie (około 11 MB przy pełnym limicie).
- Na dysku: jeden plik JSONL na dzień w katalogu danych, zapis co 5 minut i przy wyłączaniu, żeby oszczędzać kartę SD. Domyślnie 90 dni albo 1 GB.
- Po restarcie: ostatnie 24 h wczytują się z dysku, więc
lastChange()działa od razu, bez fałszywych zmian0 → wartość.
Limity zmienisz na karcie Ustawienia na stronie Narzędzia → Historia i audyt. Działają od razu. Włączenie lub wyłączenie samej historii albo zapisu na dysk wymaga restartu.
W Lua#
local light = _:get("CLU220000592.DOU4992")
local last = light:lastChange() -- {value, old, atMs, origin, actor, correlationId} albo nil
print(light:since()) -- ile sekund w obecnym stanie
for i, e in ipairs(light:history(5)) do -- 5 ostatnich, najnowsza pierwsza
print(e.value, e.actor)
endOdróżnienie własnych zmian od cudzych i działanie pod własnym imieniem:
-- Ktoś (nie my) zgasił światło: przerwa na 10 minut
StateBus:watch("CLU220000592.DOU4992", function(e)
if e.actor == "automation:wiatrolap" then return end
pauseUntil = os.time() + 600
end, { from = 1, to = 0 })
-- Zmiany zrobione w tej funkcji mają sprawcę automation:wiatrolap
StateBus:watch("satel.zone.2", function()
_:get("CLU220000592.DOU4992"):switchOn(60 * 1000)
end, { to = 1, runAs = "automation:wiatrolap" })runAs zmienia tylko sprawcę (actor). origin zostaje z wywołującego zdarzenia, np. physical dla czujki Satela.
Obiekt, którego zmian nie chcesz zapisywać, wystawiasz z history = false. Czujnik, który szumi, z filter. Oba opisane są w expose API.
W panelu#
Narzędzia → Historia i audyt: przeglądanie zmian z filtrami po obiekcie, źródle, sprawcy, tekście i czasie. Na tej samej stronie widać zużycie pamięci i dysku, błędy zapisu i ustawienia limitów.
REST API#
GET /api/history?path=CLU220000592.DOU4992&since=2026-10-08T21:00:00%2B02:00&limit=50Filtry: path, origin, actor, q (tekst), since, until (RFC 3339 albo Unix w ms), limit (domyślnie 100, maks. 1000), cursor (wartość next z poprzedniej odpowiedzi). Status i ustawienia: GET /api/history/status, GET/PUT /api/history/settings. Zmiana ustawień wymaga zalogowania w panelu, klucz API dostaje 403.
Agent AI: jak żyje dom#
Boty MCP czytają historię narzędziem vclu_device_history z tymi samymi filtrami (path, origin, actor, query, since, until, limit - domyślnie 50, cursor). Bot widzi tylko obiekty, na które pozwala mu jego profil dostępu. Dzięki temu można zapytać dom, co się w nim działo:
Ty: Jak wyglądał wczorajszy wieczór w domu?
AI: → vclu_device_history(since="2026-10-08T17:00:00+02:00", until="2026-10-09T01:00:00+02:00")
Wczoraj wieczorem:
• 17:41 - alarm rozbrojony (Satel), minutę później ruch w wiatrołapie:
ktoś wrócił do domu.
• 17:44-23:10 - kuchnia i salon: światła przełączane na CLU (włączniki),
najdłużej świeciła sofa (4 h 20 min).
• 19:30 - trzy światła w kuchni zmienione jedną akcją z tabletu
(dashboard:kuchnia).
• 22:05 - tryb nocny włączony przez automatykę.
• 23:12 - ostatnie światło zgaszone, 23:15 alarm uzbrojony.
• W nocy: dwa razy ruch w wiatrołapie (01:20, 03:05), światło zapaliła
automatyka na minutę i samo zgasło.To przykład. Agent widzi tylko obiekty, wartości i sprawców w formie z tabeli wyżej. Imion domowników historia nie zapisuje, a „ktoś wrócił do domu” to wniosek agenta z kolejności zdarzeń.