Wszystko się zazębia: jeden panel, cały dom#
Ten tutorial nie dodaje nowego urządzenia. Pokazuje, że to, co już masz w vCLU, działa razem. Światła z Grentona, czujnik Zigbee, gniazdko Tasmota, rekuperator z wtyczki i pogoda z internetu trafiają na jeden Smart Panel, a każdy z nich to najwyżej kilka linijek.
Co zbudujemy#
Na Smart Panelu powstanie menu, w którym znajdą się:
- Pokój: światła z Grentona, lampka podłączona przez Tasmotę i termometr.
- Wykres temperatury: jak zmieniała się temperatura przez ostatnie 12 godzin, z danych zapisanych przez vCLU.
- Rekuperator: bieg i temperatura zadana, których nie da się ustawić poza bezpiecznym zakresem, oraz odczyt nawiewu i czerpni.
- Pogoda: własna strona panelu, czyli kilka linijek Lua, które rysują dane z wtyczki Weather.

Czego potrzebujesz#
| Co | Gdzie |
|---|---|
| Smart Panel sterowany przez vCLU | tutorial Smart Panel |
Model domu z expose (nazwa i pomieszczenie) | tutorial Grenton |
| Opcjonalnie: Zigbee, Tasmota, rekuperator, pogoda | Zigbee, Tasmota, Pogoda |
Każdy z tych klocków jest osobny. Brakuje któregoś? Panel po prostu go nie pokaże.
1. Pokój na panelu#
expose wystawia urządzenie na zewnątrz: do Home Assistanta, do Apple Home i na panel. Podajesz w nim nazwę urządzenia i pomieszczenie (area). Nie ma znaczenia, czy urządzenie jest z Grentona, z Tasmoty czy z Zigbee. Szczegóły w dokumentacji expose().
-- modules/dom.lua: Grenton
expose(_:get("CLU221000290.DOU4157"), "light", { name = "Światło", area = "Biuro" })
expose(_:get("CLU220000592.ONE2545"), "temperature", { name = "Temperatura", area = "Biuro" })
-- modules/tasmota/init.lua: gniazdko Tasmota przez MQTT
expose(lampka, "light", { path = "tasmota.biurko", name = "Lampka biurko", area = "Biuro" })
-- modules/zigbee/salon_czujnik.lua: czujnik z Zigbee2MQTT
expose(temp, "temperature", { path = "zigbee.salon.temperatura", name = "Temperatura (czujnik)", area = "Salon" })Gniazdko z Tasmotą wystaw jako light (albo dodaj panel = true), bo przełączników pokój na panelu domyślnie nie pokazuje. Czujnik Zigbee z przykładu trafi do menu salonu.
Pokój na panelu to jedna linia:
local Panel = require("lib/panel")
local biuro = Panel.area("Biuro")Menu pokoju pokazuje światła, ściemniacze, rolety i czujniki temperatury, które w expose mają pomieszczenie Biuro: tu światła z Grentona, lampkę z Tasmoty, ściemniacz i termometr. Światło przełączasz jednym OK.

Działa to w obie strony:
- Gdy ktoś zgasi światło w Apple Home albo przyciskiem na ścianie, wiersz na panelu od razu pokazuje
OFF. Zmiany z Grentona przychodzą dla obiektów z listy Grenton → Sync. - Każda zmiana trafia do historii vCLU razem z tym, kto ją zrobił: CLU panelu, dashboard, Home Assistant, Apple Home czy przycisk na ścianie.
2. Historia termometru#
Nic nie trzeba dopisywać. Wiersz z temperaturą w menu pokoju po OK otwiera wykres z ostatnich 12 godzin: słupek na godzinę, a przyciski góra i dół wybierają godzinę.

Dane biorą się z historii, którą vCLU prowadzi dla każdego obiektu w rejestrze. Nie trzeba ani InfluxDB, ani własnych metryk. vCLU trzyma w pamięci ostatnią dobę i po restarcie wczytuje ją z dysku, więc wykres nie znika przy restarcie. Dłuższa historia to temat dla InfluxDB i Grafany.
3. Rekuperator z wtyczki#
Rekuperator Salda łączy się z vCLU przez wtyczkę. Moduł rekuperator jako jedyny korzysta z wtyczki i pilnuje bezpiecznego zakresu (biegi 1-3, temperatura 19-22 °C). Panel bierze funkcje z modułu:
local reku = require("rekuperator")
local rekuperator = Panel.menu("Rekuperator")
:number("Bieg", reku.bieg, reku.setBieg,
{ step = 1, min = reku.BIEGI[1], max = reku.BIEGI[#reku.BIEGI], format = "%d" })
:number("Zadana", reku.zadana, reku.setZadana,
{ step = 1, min = reku.ZADANA_MIN, max = reku.ZADANA_MAX, format = "%d C" })
:value("Nawiew", reku.nawiew, "%.1f C")
:value("Czerpnia", reku.zewn, "%.1f C")
minimaxbiorą zakres z modułu, więc z panelu też nie da się go przekroczyć.- Wartość jest zapisywana raz, po OK, a nie przy każdym
+. - Rekuperator odpowiada przez HTTP z opóźnieniem. Panel do tego czasu (najwyżej 90 s) pokazuje ustawioną wartość, a nie starą.
Te same funkcje modułu zasilają dashboard rekuperatora i eksport do InfluxDB. Panel jest kolejnym odbiorcą, nic nie jest liczone dwa razy.
4. Własna strona z pogodą#
Strona panelu to tabela z funkcją draw. Moduł pogoda z tutorialu pogody daje odczyty, a strona je rysuje:
local pogoda = require("pogoda")
local pogodaPage = { every = 300 } -- odświeżenie co 5 minut
pogodaPage.draw = function(s)
Panel.drawTitle(s, "Pogoda")
local t = pogoda.temp()
s:textCenter(14, pogoda.condition() or "--")
s:textCenter(26, t and string.format("%.1f C", t) or "--", 2) -- czcionka 2: większa
s:textCenter(44, pogoda.windText() or "")
end
Na stronach menu prawy górny przycisk to zawsze „wstecz”.
5. Całość#
-- modules/panel_biuro/init.lua
local Panel = require("lib/panel")
local reku = require("rekuperator")
if type(reku) ~= "table" then reku = nil end -- bez wtyczki moduł zwraca nil
local pogoda = require("pogoda")
if type(pogoda) ~= "table" then pogoda = nil end
if not Panel.find("PAN1085") then return nil end
Panel.lang("pl")
local menu = Panel.menu("Menu", { Panel.area("Biuro") })
if pogoda then
local page = { every = 300 }
page.draw = function(s)
Panel.drawTitle(s, "Pogoda")
local t = pogoda.temp()
s:textCenter(14, pogoda.condition() or "--")
s:textCenter(26, t and string.format("%.1f C", t) or "--", 2)
s:textCenter(44, pogoda.windText() or "")
end
menu:item("Pogoda", page)
end
if reku then
menu:submenu(Panel.menu("Rekuperator")
:number("Bieg", reku.bieg, reku.setBieg,
{ step = 1, min = reku.BIEGI[1], max = reku.BIEGI[#reku.BIEGI], format = "%d" })
:number("Zadana", reku.zadana, reku.setZadana,
{ step = 1, min = reku.ZADANA_MIN, max = reku.ZADANA_MAX, format = "%d C" })
:value("Nawiew", reku.nawiew, "%.1f C")
:value("Czerpnia", reku.zewn, "%.1f C"))
end
local M = {}
M.panel = Panel.new("PAN1085")
:home(Panel.corners{
tl = { "Swiatlo", "CLU221000290.DOU4157" },
tr = { "LED", "CLU221000290.DOU3328" },
bl = { "Lampka", "tasmota.biurko" },
br = { "Menu", function(p) p:open(menu) end },
clock = true,
})
:sleepAfter(30)
return MRóg sprawdza ścieżkę urządzenia przy starcie modułu, więc moduł tasmota musi się załadować wcześniej: w modules/init.lua require("tasmota") stoi wyżej niż require("panel_biuro").
Moduł panelu nie wie, skąd pochodzą urządzenia. Grenton, Tasmota, Zigbee, rekuperator i pogoda spotykają się w rejestrze, a panel tylko je pokazuje. Na tych samych danych zbudujesz dashboard albo automatykę, a agent AI może o nie zapytać.