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żywaj on()/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()#

MetodaCelPrzykład
_:get(path)Pobranie obiektu z rejestrulocal 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_changedWywoł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ówfor _, 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 = 3

Metody:

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 fn

DIN - 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 ms

Metody: 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 = 3
timer: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 CLU

ANALOG_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 = 0

RemoteObject - 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 Managera

Rysowanie 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łaArgumentyCo robi
RemotePanel.CLEAR—czyści ekran
RemotePanel.PRINT_TEXTx, y, tekst, czcionkatekst; y to górna krawędź, czcionka 1–3 (10, 14, 28 punktów), do 15 znaków
RemotePanel.PRINT_FLOATx, y, liczba, precyzja, czcionkaliczba z 0–2 miejscami po przecinku
RemotePanel.DRAW_LINEx, y, xe, ye, kolorlinia; kolor 1 biały, 0 czarny
RemotePanel.DRAW_POINTx, y, kolorpunkt
RemotePanel.DRAW_ICONx, y, plikikona .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 _:event

open() 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()                      -- maximum

RemoteGroup#

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 expose

lib/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
FunkcjaOpis
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.historywykres 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 eventy

StateBus#

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)  -- debounce

Eventy 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)