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ż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"):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()#
| 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: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 pozycjiRemoteSensor (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(). 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 exposeEventBus#
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(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) -- 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):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)