Lua API#
Kompletna dokumentacja API Lua dostępnego w runtime vCLU.
Szybki start - praktyczne przykłady#
Odczyt stanu#
-- Lampa (DOUT/RemoteSwitch) - 0=OFF, 1=ON
local lamp = _:get("CLU.DOUT_01")
local state = lamp:getValue() -- 0 lub 1
print("Lampa: " .. (state == 1 and "ON" or "OFF"))
-- Dimmer (RemoteLight) - 0.0–1.0
local light = _:get("CLU.DIMMER_01")
print("Jasność: " .. tostring(light:getBrightness()) .. "%") -- 0–100; nil przed pierwszym raportem
-- Roleta (RemoteCover) - pozycja 0=zamknięta, 100=otwarta (jak w Grentonie)
local cover = _:get("CLU.ROLLER_01")
print("Pozycja: " .. tostring(cover:getPosition())) -- nil przed raportem i dla rolety bez pozycji
print("Ruch: " .. tostring(cover:getState())) -- 0 stop, 1 w górę, 2 w dół
-- Czujnik (DIN/RemoteSensor) - 0/1
local sensor = _:get("CLU.DIN_01")
print("Czujnik: " .. sensor:getValue())
-- GPIO - tak samo
local relay = _:get("gpio.RELAY1")
print("Relay: " .. relay:get(DOUT.FEATURE_VALUE))Sterowanie wyjściami#
local lamp = _:get("CLU.DOUT_01")
-- Proste sterowanie
lamp:on() -- włącz
lamp:off() -- wyłącz
lamp:toggle() -- przełącz
-- Z czasem (pulse) - włącz na 5 sekund, potem wyłącz
lamp:switchOn(5000)
-- Dimmer
local light = _:get("CLU.DIMMER_01")
light:setBrightness(50) -- 50%
light:setBrightness(80, 2000) -- 80% z 2s rampą
light:on()
light:off()
-- Roleta
local cover = _:get("CLU.ROLLER_01")
cover:open()
cover:close()
cover:stop()
cover:setPosition(50) -- w połowie
-- Raw execute (niskopoziomowe, indeksy OM)
lamp:execute(DOUT.METHOD_SWITCH_ON) -- to samo co lamp:on()
lamp:execute(DOUT.METHOD_SWITCH_ON, 5000) -- pulse 5s
-- GPIO relay
local relay = _:get("gpio.RELAY1")
relay:execute(DOUT.METHOD_SWITCH_ON)
relay:execute(DOUT.METHOD_SWITCH, 500) -- przełącz (toggle)
sync()vs sterowanie:_:sync("CLU.DOUT_01", 1)to nie sterowanie - to aktualizacja stanu Z fizycznego CLU. Używane wewnętrznie przez OM do synchronizacji. Do sterowania używajon()/off()/toggle()/execute().
Automatyzacje - eventy#
-- Przycisk → lampa (najprostszy scenariusz)
local button = _:get("CLU.DIN_01")
local lamp = _:get("CLU.DOUT_01")
button:onClick(function()
lamp:toggle()
end)
-- Różne reakcje na rodzaj naciśnięcia
button:onClick(function() -- krótkie (<500ms)
lamp:toggle()
end)
button:onLongPress(function() -- długie (2–5s)
_:byTag("oświetlenie"):off() -- wyłącz całą grupę
end)
-- Reakcja na zmianę stanu
lamp:onChange(function(newState, oldState)
print("Lampa: " .. oldState .. " → " .. newState)
end)
-- Czujnik ruchu → światło
local motion = _:get("CLU.DIN_02")
local hallLight = _:get("CLU.DOUT_03")
motion:onSwitchOn(function()
hallLight:switchOn(60000) -- ON na 60 sekund
end)Tagi i grupy#
-- Tagowanie obiektów (w user.lua)
_:get("CLU.DOUT_01"):addTag("oświetlenie", "salon")
_:get("CLU.DOUT_02"):addTag("oświetlenie", "kuchnia")
_:get("CLU.DOUT_03"):addTag("oświetlenie", "korytarz")
-- Sterowanie grupą
_:byTag("oświetlenie"):on() -- włącz wszystkie
_:byTag("oświetlenie"):off() -- wyłącz wszystkie
_:byTag("salon"):toggle() -- przełącz salon
-- Expose całej grupy
_:byTag("oświetlenie"):each(function(o) expose(o, "switch", {area = "Dom"}) end)
-- Iteracja
_:byTag("oświetlenie"):each(function(obj)
print(obj._name .. " = " .. tostring(obj:getValue()))
end)Timery i opóźnienia#
-- Jednorazowy (po 5 sekundach)
setTimeout(function()
lamp:off()
end, 5000)
-- Powtarzający (co 10 sekund)
local id = setInterval(function()
print("tick")
end, 10000)
clearInterval(id) -- anuluj
-- Skróty
after(5000, function() lamp:off() end) -- = setTimeout
local id = every(10000, function() end) -- = setInterval
cancel(id) -- = clearInterval
-- Sekwencja
sequence()
:wait(1000)
:add(function() lamp:on() end)
:wait(3000)
:add(function() lamp:off() end)
:run()Sceny#
-- Definicja
scene("wieczór", function()
_:byTag("oświetlenie"):off()
_:get("CLU.DIMMER_01"):setBrightness(30, 2000)
end)
-- Wywołanie
runScene("wieczór")
-- Z przycisku
_:get("CLU.DIN_05"):onClick(function()
runScene("wieczór")
end)Expose do Home Assistant / HomeKit#
-- Lampa
expose(_:get("CLU.DOUT_01"), "switch", {name = "Lampa salon"})
-- Dimmer
expose(_:get("CLU.DIMMER_01"), "dimmer", {name = "Oświetlenie kuchnia"})
-- Roleta
expose(_:get("CLU.ROLLER_01"), "cover", {name = "Roleta sypialnia"})
-- Czujnik ruchu
expose(_:get("CLU.DIN_02"), "motion", {name = "Ruch korytarz"})
-- GPIO relay
expose(_:get("gpio.RELAY1"), "switch", {name = "Przekaźnik bramka"})
-- Sensor z pluginu
local weather = Plugin.getPlugin("@vclu/weather")
if weather then
expose(weather:get("temperature"), "temperature", {
name = "Temperatura zewnętrzna", unit = "°C"
})
end
-- Cała grupa
_:byTag("oświetlenie"):each(function(o) expose(o, "switch", {area = "Dom"}) end)Referencja API#
_:get() vs _:sync() vs _:event()#
| Metoda | Cel | Przykład |
|---|---|---|
_:get(path) | Pobranie obiektu z rejestru | local lamp = _:get("CLU.DOUT_01") |
_registry[path] | To samo co _:get() | local lamp = _registry["CLU.DOUT_01"] |
_:sync(path, val) | Aktualizacja stanu z zewnętrznego CLU + emit state_changed | Wywoływane przez OM, nie przez użytkownika |
_:event(path) | Emisja eventu na obiekcie | _:event("CLU.DOUT_01.OnSwitchOn") |
_:byTag(tag) | Grupa obiektów po tagu | _:byTag("lights"):on() |
_:list() | Lista wszystkich obiektów | for _, o in ipairs(_:list()) do ... end |
_:find(pattern) | Wyszukiwanie po wzorcu | _:find("DOUT") |
_:tags() | Lista wszystkich tagów | _:tags() |
Moduły obiektowe#
DOUT - wyjście cyfrowe#
DOUT.FEATURE_VALUE = 0 -- stan: 1=ON, 0=OFF
DOUT.FEATURE_VOLTAGE_TYPE = 3 -- 0=AC, 1=DC, 2=Signal
DOUT.FEATURE_VOLTAGE_VALUE = 4
DOUT.FEATURE_POWER = 5 -- moc chwilowa (read-only)
DOUT.FEATURE_OVERLOAD = 7 -- próg przeciążenia
DOUT.METHOD_SWITCH = 0 -- toggle
DOUT.METHOD_SWITCH_ON = 1 -- włącz (opcjonalny param: czas ms)
DOUT.METHOD_SWITCH_OFF = 2 -- wyłącz
DOUT.EVENT_ON_VALUE_CHANGE = 0
DOUT.EVENT_ON_SWITCH_ON = 1
DOUT.EVENT_ON_SWITCH_OFF = 2
DOUT.EVENT_ON_OVERLOAD = 3Metody:
dout:get(featureIndex) -- odczyt feature
dout:set(featureIndex, value) -- zapis feature
dout:execute(methodIndex, time) -- wykonaj metodę (time opcjonalny, ms)
dout:add_event(eventId, cb) -- rejestracja event handlera
dout:onChange(cb) -- callback na zmianę wartości, zwraca unsubscribe fnDIN - wejście cyfrowe#
DIN.FEATURE_VALUE = 0 -- stan 0/1 (read-only)
DIN.FEATURE_INTERTION = 1 -- debounce (ms)
DIN.FEATURE_HOLD_DELAY = 2 -- opóźnienie OnHold (ms)
DIN.FEATURE_HOLD_INTERVAL = 3 -- interwał OnHold (ms)
DIN.EVENT_ON_CHANGE = 0
DIN.EVENT_ON_SWITCH_ON = 1
DIN.EVENT_ON_SWITCH_OFF = 2
DIN.EVENT_ON_SHORT_PRESS = 3 -- 500–2000 ms
DIN.EVENT_ON_LONG_PRESS = 4 -- 2000–5000 ms
DIN.EVENT_ON_HOLD = 5
DIN.EVENT_ON_CLICK = 6 -- < 500 msMetody: get(), set(), add_event(), onChange() - identycznie jak DOUT.
Timer#
Timer.FEATURE_TIME = 0 -- okres w ms (R/W)
Timer.FEATURE_MODE = 1 -- tryb (R/W)
Timer.FEATURE_STATE = 2 -- stan (R)
Timer.FEATURE_VALUE = 3 -- pozostały czas ms (R)
Timer.MODE_COUNT_DOWN = 0 -- jednorazowy
Timer.MODE_INTERVAL = 1 -- powtarzający
Timer.STATE_STOPPED = 0
Timer.STATE_COUNTING = 1
Timer.STATE_PAUSED = 2
Timer.METHOD_START = 0
Timer.METHOD_STOP = 1
Timer.METHOD_PAUSE = 2
Timer.EVENT_ON_TIMER = 0
Timer.EVENT_ON_START = 1
Timer.EVENT_ON_STOP = 2
Timer.EVENT_ON_PAUSE = 3timer:set(Timer.FEATURE_TIME, 5000) -- ustaw 5 sekund
timer:set(Timer.FEATURE_MODE, Timer.MODE_INTERVAL)
timer:execute(Timer.METHOD_START)
timer:add_event(Timer.EVENT_ON_TIMER, function()
print("tick!")
end)CLU#
CLU.FEATURE_UPTIME = 0 -- sekundy od startu
CLU.FEATURE_CLIENT_REPORT_INTERVAL = 1
CLU.FEATURE_DATE = 2 -- "YYYY-MM-DD"
CLU.FEATURE_TIME = 3 -- "HH:MM:SS"
CLU.FEATURE_DAY_OF_MONTH = 4 -- 1–31
CLU.FEATURE_MONTH = 5 -- 1–12
CLU.FEATURE_YEAR = 6
CLU.FEATURE_DAY_OF_WEEK = 7 -- 0=niedziela
CLU.FEATURE_HOUR = 8 -- 0–23
CLU.FEATURE_MINUTE = 9 -- 0–59
CLU.FEATURE_TIMESTAMP = 10 -- Unix
CLU.FEATURE_TIME_ZONE = 11 -- R/W
CLU.FEATURE_VCLU_CLIENT_TTL = 100 -- TTL sesji klientów (s)
CLU.EVENT_ON_INIT = 0
CLU.METHOD_EXECUTE = 0 -- wykonaj Lua na zdalnym CLUANALOG_IN / ANALOG_OUT#
ANALOG_IN.FEATURE_VALUE = 0 -- wartość analogowa (R)
ANALOG_IN.FEATURE_VALUE_PERCENT = 1 -- procent 0–100% (R)
ANALOG_IN.FEATURE_SCALE = 2 -- mnożnik
ANALOG_IN.FEATURE_SENSITIVITY = 3
ANALOG_IN.FEATURE_MIN_VALUE = 5
ANALOG_IN.FEATURE_MAX_VALUE = 6
ANALOG_IN.EVENT_ON_CHANGE = 0
ANALOG_OUT.FEATURE_VALUE = 0 -- wartość (R/W)
ANALOG_OUT.FEATURE_RAMP = 3 -- czas rampy (ms)
ANALOG_OUT.METHOD_SET_VALUE = 0
ANALOG_OUT.EVENT_ON_CHANGE = 0RemoteObject - obiekty zdalne#
Obiekty z innych CLU (importowane przez OM) dziedziczą z RemoteObject i mają rozszerzone API.
Wspólna baza#
obj:get(featureIndex)
obj:set(featureIndex, value)
obj:execute(methodIndex, param)
obj:onEvent(eventName, callback) -- rejestracja po nazwie zdarzenia
obj:emit(eventName, ...) -- emituj event
obj:onChange(callback) -- subskrypcja zmian, zwraca unsubscribe fn
obj:getInfo() -- {id, name, type, tags, methods}
obj:expose(options) -- wystaw (typ z klasy obiektu); obj:unexpose(), obj:isExposed()
obj:addTag(tag1, tag2, ...)
obj:hasTag(tag)
obj:getTags()
obj:removeTag(tag)get(i) pyta CLU i czeka na odpowiedź; set i execute wysyłają komendę bez czekania.
RemoteSwitch (DOUT)#
switch:getValue() -- 0 lub 1
switch:on() -- włącz
switch:off() -- wyłącz
switch:toggle() -- przełącz
switch:switchOn(durationMs) -- ON na N ms, potem OFF
switch:switchOff(ms) -- wyłącz (ms: czas jak w Grentonie)
switch:onSwitchOn(cb) -- event: włączenie
switch:onSwitchOff(cb) -- event: wyłączenie
switch:onChange(cb) -- event: zmiana stanu (newState, oldState)RemoteLight (DIMMER)#
light:getValue() -- 0.0–1.0
light:getBrightness() -- 0–100
light:setValue(v, rampMs) -- ustaw jasność (0.0–1.0); rampa zostaje dla kolejnych zmian
light:setBrightness(pct, rampMs) -- ustaw jasność (0–100%) z rampą
light:on()
light:off()
light:toggle()
light:dim(rampMs) -- ściemnianie jak przy przytrzymaniu (Hold)
light:onSwitchOn(cb)
light:onSwitchOff(cb)
light:onChange(cb)RemoteRGBLight (LEDRGB)#
Dziedziczy z RemoteLight + dodatkowe metody:
rgb:getHue() -- 0–360 stopni
rgb:setHue(hue, rampMs)
rgb:getSaturation() -- 0.0–1.0
rgb:setSaturation(sat, rampMs)
rgb:getRGB() -- kolor z CLU, przemnożony przez jasność ("#7f0000" przy 50%)
rgb:setRGB(hex, rampMs) -- kolor wprost; jasność ustawi się z najjaśniejszego kanału
rgb:setColor(r, g, b, rampMs) -- ustaw kolor (0–255), jasność zostaje (wyłączone: włącza na 100%)
rgb:getColor() -- r, g, b (0–255) albo nil przed pierwszym raportem
rgb:getWhite() -- biel modułu RGBW (0–255)
rgb:setWhite(value, rampMs) -- ustaw biel (0–255)CLU podaje kolor przemnożony przez jasność (czerwony przy 50% to #7f0000). vCLU dzieli go przez jasność, więc getColor() zwraca kolor światła, a wyłączone światło zachowuje ostatni kolor. Biel ma tylko światło z modułu LED RGBW (import OM oznacza je sam).
RemoteLedChannel (LED_CHANNEL)#
Jeden kanał (R, G, B albo W) modułu LED RGBW 2.0. Działa jak RemoteLight: wartość 0.0–1.0, getBrightness()/setBrightness(pct) 0–100%. vCLU przelicza ją na skalę modułu 0–255.
RemoteThermostat (THERMOSTAT)#
Termostat CLU (obiekty THE… z Object Managera). Wartość obiektu to temperatura na czujniku termostatu.
th:getValue() -- temperatura na czujniku (°C)
th:getTarget() -- zadana, która obowiązuje (też z harmonogramu i trybu wakacyjnego)
th:getSetpoint() -- nastawa ręczna (PointValue)
th:setSetpoint(v) -- ustaw nastawę ręczną; obowiązuje w trybie ręcznym
th:getState() -- 1 pracuje, 0 zatrzymany
th:getMode() -- 0 ręczny, 1 wakacyjny, 2 automatyczny, 3 podgrzewanieŻeby vCLU dostawało wartości, termostat musi być na liście Grenton → Sync. Termostatu nie da się jeszcze wystawić do Home Assistanta ani HomeKita (expose() go odrzuca).
Na CLU v1.0 (firmware 4.x) dodanie termostatu do listy Sync resetuje CLU przy rejestracji subskrypcji. Do czasu poprawki (#67) nie dodawaj tam termostatów z takiego CLU.
RemotePanel (PANEL) i RemotePanelButton (PANEL_BUTTON)#
Smart Panel z importu OM: ekran i przyciski. Zwykle steruje nim biblioteka lib/panel (niżej), ale metody są dostępne bezpośrednio:
local p = require("lib/panel").find("PAN1085") -- albo obiekt z importu
local btn = p:getButton(0)
p:beep(freq, ms, volume) -- sygnał dźwiękowy
p:displayOn() -- włącz ekran
p:getButton(0) -- przycisk 0–3 (strona 1): RemotePanelButton
p:send({ p:op(RemotePanel.CLEAR), p:op(RemotePanel.PRINT_TEXT, 0, 0, "Czesc", 2), p:op(RemotePanel.DISPLAY) })
p:setPage(nr) -- wbudowana strona Grentona (1–4)
p:onInput(function(input) end) -- "btn1", "btn1:long", "wake"...; zastępuje obsługę z Panel.new
btn:ledOn() -- dioda przycisku
btn:ledOff()
btn:setLabel(text)
RemotePanel.click(panelOrButtonId, input) -- to, co woła skrypt click z Object ManageraRysowanie na najniższym poziomie: każda metoda ekranu z Object Managera ma stałą, p:op(stała, argumenty...) buduje z niej jedno polecenie, a p:send({...}) wysyła całą ramkę. Ramka zaczyna się od CLEAR i kończy DISPLAY.
| Stała | Argumenty | Co robi |
|---|---|---|
RemotePanel.CLEAR | — | czyści ekran |
RemotePanel.PRINT_TEXT | x, y, tekst, czcionka | tekst; y to górna krawędź, czcionka 1–3 (10, 14, 28 punktów), do 15 znaków |
RemotePanel.PRINT_FLOAT | x, y, liczba, precyzja, czcionka | liczba z 0–2 miejscami po przecinku |
RemotePanel.DRAW_LINE | x, y, xe, ye, kolor | linia; kolor 1 biały, 0 czarny |
RemotePanel.DRAW_POINT | x, y, kolor | punkt |
RemotePanel.DRAW_ICON | x, y, plik | ikona .bmp z karty panelu, nazwa bez rozszerzenia |
RemotePanel.DISPLAY | — | pokazuje narysowaną ramkę |
RemotePanel.SWITCH_ON_DISPLAY | — | włącza ekran |
Ekran ma 128 × 64 punkty i jest czarno-biały. Polskie litery trzeba zamienić samemu (require("lib/panel").map(tekst)), bo panel ich nie ma.
send dzieli ramkę na krótkie wiadomości i wysyła je z odstępami, tak jak CLU panelu je przyjmuje. Nie wysyłaj do panelu własnych skryptów w pętli.
RemoteCover (ROLLER)#
cover:getState() -- ruch: 0 stop, 1 w górę, 2 w dół, 3 blokada, 4 kalibracja; nil przed raportem
cover:getPosition() -- 0 zamknięta .. 100 otwarta; nil przed raportem i bez pozycji (ROLLER_SH)
cover:getTilt() -- na razie zawsze nil (vCLU nie subskrybuje kąta lameli)
cover:setTilt(angle) -- ustaw kąt lameli
cover:open(durationMs) -- otwórz (opcjonalny czas)
cover:close(durationMs) -- zamknij
cover:stop() -- zatrzymaj
cover:up(durationMs) -- alias open()
cover:down(durationMs) -- alias close()
cover:setPosition(pos, tilt) -- ustaw pozycję (0–100), kąt lameli domyślnie bez zmian (91)
cover:onChange(cb) -- zmiana pozycji (new, old)
cover:onEvent("OnMovementChange", cb) -- start i koniec ruchu (movement, old)
cover:onUp(cb), cover:onDown(cb), cover:onStop(cb) -- tylko gdy skrypt CLU przekazuje zdarzenie przez _:eventopen() i close() bez czasu ustawiają pozycję 100 albo 0, także gdy roleta jest w ruchu. Z czasem wysyłają MoveUp/MoveDown, które zatrzymują roletę w ruchu.
VirtualCover#
Roleta bez sprzętu, ta sama skala co RemoteCover: getPosition() (domyślnie 0), setPosition(pos), open() (100), close() (0), stop(), up(), down(), onPositionChange(cb).
RemoteSensor (DIN)#
sensor:getValue() -- 0 lub 1
sensor:onChange(cb) -- event: zmiana stanu
sensor:onSwitchOn(cb) -- event: aktywacja (0→1)
sensor:onSwitchOff(cb) -- event: deaktywacja (1→0)
sensor:onClick(cb) -- event: kliknięcie (<500ms)
sensor:onLongPress(cb) -- event: długie naciśnięcie (2–5s)
sensor:onHold(cb) -- event: trzymanie (powtarzane)RemoteNumber (AnalogOUT)#
num:getValue() -- aktualna wartość
num:setValue(value) -- ustaw wartość
num:getMin() -- minimum
num:getMax() -- maximumRemoteGroup#
Zwracana przez _:byTag() i _:byTags(). Wywołania metod są przekazywane do każdego obiektu:
local group = _:byTag("oświetlenie")
group:on() -- włącz wszystkie
group:off() -- wyłącz
group:toggle() -- przełącz
group:count() -- ile obiektów
group:each(function(obj, i) end) -- iteracja
group:filter(function(obj) return obj:hasTag("salon") end) -- filtruj
group:each(function(o) expose(o, "switch", {area = "Dom"}) end) -- expose całej grupy
group:unexpose() -- cofnij exposelib/panel - Smart Panel#
Biblioteka, która rysuje ekran Smart Panelu i obsługuje jego przyciski. Ładujesz ją w module: local Panel = require("lib/panel"). Pełny opis: Smart Panel, krok po kroku: tutorial Smart Panel.
local Panel = require("lib/panel")
Panel.lang("pl") -- teksty biblioteki po polsku
local reku = require("rekuperator") -- moduł z funkcjami rekuperatora
local menu = Panel.menu("Dom", { -- menu z podmenu
Panel.area("Salon"), -- pokój z expose (area)
Panel.floor("Parter"), -- piętro z expose (floor)
})
menu:add("CLU220000592.DOU0789") -- wiersz dobrany do urządzenia
:gate("Brama", "CLU220000592.DIN0095", { open = 0 })
:number("Zadana", reku.zadana, reku.setZadana, { step = 1, min = 19, max = 22 }) -- funkcje modułu rekuperatora
:action("Czyszczenie", Panel.cleaning(30))
Panel.new("PAN1085") -- panel z importu OM
:home(Panel.corners{ -- każdy róg to jeden przycisk
tl = { "Swiatlo", "CLU221000290.DOU4157" },
br = { "Menu", function(p) p:open(menu) end },
clock = true,
})
:sleepAfter(30) -- po 30 s bez dotyku wraca na start| Funkcja | Opis |
|---|---|
Panel.new(id) | przejmuje panel z importu OM; :buttons, :back, :home, :idle, :name, :sleepAfter, :onGesture, :onProximity |
Panel.find(id) | panel po identyfikatorze lub nazwie, albo nil |
Panel.menu(tytuł, podmenu) | menu; wiersze :add, :toggle, :dimmer, :cover, :gate, :thermostat, :value, :number, :choice, :item, :submenu, :action |
Panel.area(pokój, opcje) | menu z urządzeń, które expose przypisał do pomieszczenia; title, kinds, skip |
Panel.floor(piętro, opcje) | menu z pokoi piętra; order, titles |
Panel.corners{...} | ekran z czterema przyciskami w rogach, clock = true rysuje godzinę |
Panel.chart, Panel.history | wykres z 12 h i lista ostatnich zmian obiektu |
Panel.cleaning(s), panel:notify(tekst) | blokada do mycia szyby, komunikat na ekranie |
Panel.register(nazwa, fabryka) | własny typ strony |
Własna strona rysuje przez obiekt screen, który dostaje w draw(s, panel). Liczy szerokość czcionek, wyrównuje i przycina tekst oraz zamienia polskie litery:
local page = { every = 60 } -- przerysowanie co minutę
page.draw = function(s)
Panel.drawTitle(s, "Salon") -- tytuł między podpowiedziami rogów
s:text(0, 14, "Temperatura") -- x, y (górna krawędź), tekst, czcionka 1–3
:textRight(14, "21.5") -- do prawej krawędzi
s:textCenter(26, "Pochmurno", 2) -- na środku, większa czcionka
s:rule(40) -- pozioma linia przez cały ekran
s:line(0, 52, 30, 52) -- linia od x, y do xe, ye
s:point(64, 60)
s:float(90, 44, 21.53, 1) -- liczba z precyzją 0–2
s:icon(0, 0, "temp") -- ikona z karty panelu
end
menu:item("Salon", page)EventBus#
local eb = EventBus:getShared()
eb:on(module, eventName, callback) -- rejestracja (moduł)
eb:on(eventName, callback) -- rejestracja (string)
eb:fire(module, eventName) -- emituj (moduł)
eb:emit(eventName, arg1, arg2, ...) -- emituj (string)
eb:off(module, eventName, callback) -- wyrejestruj
eb:setTimeout(callback, ms) -- jednorazowy timer → id
eb:setInterval(callback, ms) -- powtarzający → id
eb:clearInterval(id) -- anuluj
eb:debug() -- wypisz zarejestrowane eventyStateBus#
local sb = StateBus:getShared()
sb:on(event, callback)
sb:off(event)
sb:emit(event, data)
sb:listenerCount(event)
sb:debug()Emitowane eventy: state_changed (data: {path, value, oldValue, type, obj}), object_exposed, object_unexposed, object_updated.
ExposedObjects - expose() API#
Skrócona referencja. Pełna dokumentacja: ExposedObjects - expose() API
local handle = expose(object, type, options)Typy: "switch", "light", "dimmer", "sensor", "motion", "temperature", "humidity", "cover", "scene", "number", "button", "lock", "fan", "climate", "garage_door"
-- Fluent API
expose(obj, "switch"):name("Lampa"):readonly():mqttOnly()
-- ExposedHandle
handle:getValue() -- odczyt
handle:setValue(value) -- zapis + emit state_changed
handle:unexpose() -- usunięcie
handle:executeScene() -- scena z 300ms debounce
-- Auto-expose całego rejestru
exposeRegistry()HttpClient#
local http = HTTP:new()
-- Konfiguracja (chainable)
http:setBaseUrl("https://api.example.com")
http:setTimeout(ms)
http:setBearerToken("xxx")
http:setHeader("X-Key", "val")
http:setBasicAuth(user, pass)
-- Synchroniczne
local resp = http:get(url, headers)
local resp = http:post(url, body, headers)
local resp = http:put(url, body, headers)
local resp = http:patch(url, body, headers)
local resp = http:delete(url, headers)
-- resp = {status=200, body="...", headers={...}}
-- Asynchroniczne
http:asyncGet(url, function(resp, err) end)
http:asyncPost(url, body, function(resp, err) end)
-- JSON
http:postJSON(url, table, headers)
http:putJSON(url, table, headers)
http:parseJSON(resp)HTTPServer - wbudowany serwer Lua#
HTTPServer:start(port)
HTTPServer:start(port, { auth = { username = "u", password = "p" } })
HTTPServer:stop()
HTTPServer:isRunning()
-- Rejestracja endpointu
HTTPServer:on("/api/data", {"GET", "POST"}, function(req)
-- req: {id, path, method, headers, body, query, remoteAddr}
return { status = 200, body = "OK", headers = {} }
end, { public = true })
HTTPServer:off("/api/data")Limity: 1 serwer na vCLU, domyślny port 8081, max body 1 MB, timeout 30s.
MqttClient#
local mqtt = MQTT:new(id)
mqtt:setHost(host)
mqtt:setPort(port)
mqtt:setCredentials(user, pass)
mqtt:setClientId(id)
mqtt:setTopicPrefix(prefix)
mqtt:setQoS(0|1|2)
mqtt:connect()
mqtt:disconnect()
mqtt:isConnected()
mqtt:publish(topic, payload, qos, retain)
mqtt:subscribe(topic, callback) -- callback(topic, payload, qos, retain)
mqtt:unsubscribe(topic)
mqtt:onConnect(cb)
mqtt:onDisconnect(cb)
mqtt:onMessage(cb)
mqtt:onError(cb)GPIO#
Raw API#
gpio.setup(pin, "output", "off") -- mode: "input"|"output", pull: "up"|"down"|"off"
gpio.write(pin, true)
gpio.read(pin) -- zwraca 0 lub 1
gpio.toggle(pin)
gpio.release(pin)
gpio.stats()GPIO_DOUT - przekaźnik#
local relay = GPIO_DOUT:new("Relay1", 17, {activeLow = true})
relay:execute(DOUT.METHOD_SWITCH_ON)
relay:execute(DOUT.METHOD_SWITCH_ON, 500) -- pulse: ON na 500ms
relay:get(DOUT.FEATURE_VALUE)
relay:onChange(cb)GPIO_DIN - przycisk#
local button = GPIO_DIN:new("Button1", 27, "up")
button:add_event(DIN.EVENT_ON_CLICK, function() print("click!") end)
button:add_event(DIN.EVENT_ON_LONG_PRESS, function() print("long!") end)
button:set(DIN.FEATURE_INTERTION, 50) -- debounceEventy GPIO#
EventBus:getShared():on("gpio.pin.27.OnChange", function(data)
-- data: {pin, value, oldValue}
end)
EventBus:getShared():on("gpio.OnChange", function(data) end)AccessControl#
AccessControl.get("mqtt", "CLU.DOU1") -- "full"|"readonly"|"hidden"
AccessControl.set("mqtt", "CLU.DOU1", "hidden")
AccessControl.isHidden("mqtt", path)
AccessControl.isReadonly("homekit", path)
AccessControl.canControl("mqtt", path)Globalne helpery#
-- Timery (skróty)
local id = after(ms, fn) -- setTimeout
local id = every(ms, fn) -- setInterval
cancel(id) -- clearInterval
-- Matematyka
clamp(value, min, max)
lerp(a, b, t) -- interpolacja liniowa
map(v, inMin, inMax, outMin, outMax)
round(v, decimals)
-- Sceny
scene("name", fn)
runScene("name")
-- Sekwencje
sequence():wait(ms):add(fn):run()
-- JSON
JSON:encode(table)
JSON:decode(string)
-- Logger
Logger:getShared():info(msg)
Logger:getShared():debug(msg)
Logger:getShared():warn(msg)
Logger:getShared():error(msg)