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ść: " .. light:getBrightness() .. "%")  -- 0–100

-- Roleta (RemoteCover) — pozycja 0=otwarta, 100=zamknięta
local cover = _:get("CLU.ROLLER_01")
print("Pozycja: " .. cover:getPosition())
print("Stan: " .. cover:getState())      -- 0=stop, 1=up, 2=down

-- 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)                     -- 50% zamknięcia

-- Raw execute (niskopoziomowe, indeksy Grenton)
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)    -- pulse 500ms

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"):expose("switch", {area = "Dom"})

-- Iteracja
_:byTag("oświetlenie"):each(function(obj)
    print(obj:getName() .. " = " .. obj:getValue())
end)

Timery i opóźnienia#

-- Jednorazowy (po 5 sekundach)
setTimeout(5000, function()
    lamp:off()
end)

-- Powtarzający (co 10 sekund)
local id = setInterval(10000, function()
    print("tick")
end)
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)
    :do(function() lamp:on() end)
    :wait(3000)
    :do(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 jednym wywołaniem
_:byTag("oświetlenie"):expose("switch", {area = "Dom"})

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:add_event(eventIndex, cb)
obj:on(eventName, callback)       -- rejestracja po nazwie
obj:emit(eventName, ...)          -- emituj event
obj:onChange(callback)             -- subskrypcja zmian, zwraca unsubscribe fn
obj:getType()                     -- "DOUT", "DIN", "DIMMER"...
obj:getName()
obj:addTag(tag1, tag2, ...)
obj:hasTag(tag)
obj:getTags()
obj:removeTag(tag)

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(delayMs)
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) z opcjonalną rampą
light:setBrightness(pct, rampMs)  -- ustaw jasność (0–100%) z rampą
light:on()
light:off()
light:toggle()
light:dim(rampMs)                 -- start dimming (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()                      -- "#FF5500"
rgb:setRGB(hex, rampMs)           -- ustaw kolor hex
rgb:setColor(r, g, b, rampMs)     -- ustaw kolor RGB (0–255)

RemoteCover (ROLLER)#

cover:getState()                  -- 0=stop, 1=up, 2=down
cover:getPosition()               -- 0=open, 100=closed
cover:getTilt()                   -- 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) i kąt lameli
cover:onUp(cb)                    -- event: ruch w górę
cover:onDown(cb)                  -- event: ruch w dół
cover:onStop(cb)                  -- event: zatrzymanie
cover:onPositionChange(cb)        -- event: zmiana pozycji

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(). Wywołania metod 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:expose("switch", {area = "Dom"})  -- expose całej grupy
group:unexpose()                  -- cofnij expose

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(instanceId)

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):do(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)