DOUT - wyjscie cyfrowe#

DOUT (Digital Output) to obiekt reprezentujacy wyjscie cyfrowe - przekaznik, lampe, zawor. Wystepuje w trzech wariantach:

  • Lokalny DOUT - wirtualny modul vCLU, konfigurowany w wizardzie (emuluje fizyczny modul Grenton)
  • DOUT z OM - obiekt z Object Manager, po sparowaniu z fizycznym CLU Grenton
  • GPIO_DOUT - obiekt tworzony w user.lua, sterujacy pinem GPIO Raspberry Pi

Wszystkie warianty maja ten sam interfejs API (get/set/execute/add_event) i te same stale.

Lokalny DOUT - moduly wirtualne#

Gdy vCLU dziala jako emulator CLU (tryb CLU w wizardzie), mozna dodac wirtualne moduly wyjsc. Sa to emulacje fizycznych modulow Grenton - nie steruja prawdziwym sprzetem, ale pojawiaja sie w OM i moga byc uzywane do testow, automatyzacji i integracji z Home Assistant / HomeKit.

Konfiguracja w wizardzie#

W kroku 2 wizarda (“Moduly”) mozna dodac moduly z wyjsciami cyfrowymi:

Typ moduluWyjscia DOUTWejscia DINAnalogHW Type
DOUT8T8810x1e
RELAY44-10x15

Kazdy modul dostaje unikalna nazwe (np. DOUT8T_1) i numer seryjny. Obiekty I/O sa generowane automatycznie z losowymi identyfikatorami (np. DOU5048, DOU4458).

Jak to dziala#

Wizard → .vclu.json → config.txt + CONFIG.JSON → OM → om.lua → runtime
  1. Uzytkownik dodaje modul w wizardzie
  2. vCLU zapisuje konfiguracje w .vclu.json (moduly + obiekty I/O)
  3. Generuje config.txt i CONFIG.JSON z wpisami TFBus
  4. Object Manager (OM) odczytuje konfiguracje i generuje om.lua
  5. Przy starcie Lua laduje om.lua - obiekty DOUT sa dostepne w runtime

Dostep do obiektow#

Obiekty lokalne sa dostepne przez registry, identycznie jak na prawdziwym CLU:

-- Przez nazwe z OM
local lampa = _:get("CLU.DOU5048")
lampa:execute(DOUT.METHOD_SWITCH_ON)

-- Albo przez nazwe nadana w wizardzie
local lampa = _:get("CLU.DOUT8T_1_DOU1")

.vclu.json#

Moduly i ich I/O sa zapisane w konfiguracji:

{
  "device": {
    "modules": [
      {
        "typeId": "dout8t",
        "serial": "330000027",
        "name": "DOUT8T_1",
        "hardwareType": 30
      }
    ],
    "ioObjects": [
      {
        "id": "DOU5048",
        "typeId": "dout",
        "index": 0,
        "name": "DOUT8T_1_DOU1",
        "luaType": 4
      }
    ]
  }
}
Lokalne moduly DOUT nie steruja fizycznym sprzetem - zmiany stanu sa tylko w pamieci. Ale obiekty sa w pelni funkcionalne: emituja zdarzenia, dzialaja z `expose()`, pojawiaja sie w Home Assistant i HomeKit. Do sterowania prawdziwymi przekaznikami na Raspberry Pi uzyj `GPIO_DOUT`.

GPIO_DOUT - tworzenie#

local relay = GPIO_DOUT:new(name, pin, opts)
ParametrTypOpis
namestringNazwa globalna (np. "RELAY1")
pinnumberNumer pinu BCM
optstableOpcje (opcjonalne)

Opcje#

KluczTypDomyslnieOpis
activeLowbooleanfalseInwersja logiki (LOW = ON, HIGH = OFF)
namestring-Nazwa wyswietlana
-- Przekaznik z inwersja (typowe dla plyt relay)
local relay = GPIO_DOUT:new("RELAY1", 17, {activeLow = true})

-- LED bez inwersji
local led = GPIO_DOUT:new("LED1", 22)

activeLow#

Wiekszosz plyt przekaznikowych uzywa logiki odwroconej - LOW na pinie aktywuje przekaznik, HIGH wylacza. Opcja activeLow = true automatycznie odwraca logike:

LogikaactiveLow = falseactiveLow = true
ON (value=1)pin HIGHpin LOW
OFF (value=0)pin LOWpin HIGH
safeState (przy starcie)LOWHIGH (przekaznik OFF)
Przy `activeLow = true` pin startuje w stanie HIGH (przekaznik wylaczony). To zapobiega przypadkowemu wlaczeniu przekaznika przy starcie systemu.

DOUT z OM - obiekty z Object Manager#

Obiekty DOUT z OM sa tworzone automatycznie po sparowaniu z fizycznym CLU Grenton. Dostep przez registry:

local lampa = _:get("CLU.DOUT1")
lampa:execute(DOUT.METHOD_SWITCH_ON)

Porownanie wariantow#

Lokalny DOUTDOUT z OMGPIO_DOUT
ZrodloWizard (vCLU)Object Manager (OM)user.lua
SprzetBrak (w pamieci)Fizyczny modul GrentonPin GPIO Raspberry Pi
Konfiguracja.vclu.jsonom.luaOM generuje om.luaGPIO_DOUT:new() w kodzie
ZastosowanieTestowanie, emulacjaProdukcja z prawdziwym CLUProdukcja na Raspberry Pi
APIget/set/execute/add_eventget/set/execute/add_eventget/set/execute/add_event
expose()taktaktak
exposeRegistry()tak (automatycznie)tak (automatycznie)tak (po registerObject)

Stale#

Cechy (Features)#

StalaWartoscOpis
DOUT.FEATURE_VALUE0Stan wyjscia: 1=ON, 0=OFF
DOUT.FEATURE_VOLTAGE_TYPE3Typ napiecia: 0=AC, 1=DC, 2=Signal
DOUT.FEATURE_VOLTAGE_VALUE4Wartosc napiecia
DOUT.FEATURE_POWER5Moc chwilowa (tylko odczyt)
DOUT.FEATURE_OVERLOAD7Prog przeciazenia

Metody#

StalaWartoscOpis
DOUT.METHOD_SWITCH0Przelacz (toggle)
DOUT.METHOD_SWITCH_ON1Wlacz
DOUT.METHOD_SWITCH_OFF2Wylacz

Zdarzenia#

StalaWartoscOpis
DOUT.EVENT_ON_VALUE_CHANGE0Zmiana wartosci
DOUT.EVENT_ON_SWITCH_ON1Wlaczenie
DOUT.EVENT_ON_SWITCH_OFF2Wylaczenie
DOUT.EVENT_ON_OVERLOAD3Przeciazenie

Wartosci#

StalaWartosc
DOUT.VALUE_ON / DOUT.STATE_ON1
DOUT.VALUE_OFF / DOUT.STATE_OFF0

API#

execute(method, time)#

Wykonuje metode na obiekcie. Opcjonalny parametr time (ms) powoduje automatyczne cofniecie po zadanym czasie.

-- Wlacz
relay:execute(DOUT.METHOD_SWITCH_ON)

-- Wylacz
relay:execute(DOUT.METHOD_SWITCH_OFF)

-- Przelacz (toggle)
relay:execute(DOUT.METHOD_SWITCH)

-- Wlacz na 5 sekund, potem wylacz
relay:execute(DOUT.METHOD_SWITCH_ON, 5000)

-- Wylacz na 3 sekundy, potem wlacz
relay:execute(DOUT.METHOD_SWITCH_OFF, 3000)

get(feature)#

Odczytuje wartosc cechy.

local stan = relay:get(DOUT.FEATURE_VALUE)  -- 0 lub 1

set(feature, value)#

Ustawia wartosc cechy.

relay:set(DOUT.FEATURE_VALUE, 1)  -- wlacz
relay:set(DOUT.FEATURE_VALUE, 0)  -- wylacz

add_event(event, callback)#

Rejestruje callback na zdarzenie.

relay:add_event(DOUT.EVENT_ON_SWITCH_ON, function()
    log.info("Przekaznik wlaczony!")
end)

relay:add_event(DOUT.EVENT_ON_SWITCH_OFF, function()
    log.info("Przekaznik wylaczony!")
end)

relay:add_event(DOUT.EVENT_ON_VALUE_CHANGE, function()
    local v = relay:get(DOUT.FEATURE_VALUE)
    log.info("Nowa wartosc: " .. v)
end)

onChange(callback)#

Rejestruje callback na zmiane wartosci. Zwraca funkcje unsubscribe. Uzywany przez system expose() do automatycznej aktualizacji stanow w Home Assistant i HomeKit.

local unsub = relay:onChange(function(newValue, oldValue)
    log.info("Zmiana: " .. oldValue .. " → " .. newValue)
end)

-- Pozniej: wyrejestruj callback
unsub()

Przyklady#

Przekaznik GPIO z eksponowaniem#

local relay = GPIO_DOUT:new("RELAY1", 17, {activeLow = true})
expose(relay, "switch", {name = "Lampa salon"})

Toggle na przycisk#

local relay = GPIO_DOUT:new("RELAY1", 17, {activeLow = true})
local btn = GPIO_DIN:new("BTN1", 27)

btn:add_event(DIN.EVENT_ON_CLICK, function()
    relay:execute(DOUT.METHOD_SWITCH)
end)

Wlaczenie czasowe (np. oswietlenie klatki schodowej)#

local swiatlo = GPIO_DOUT:new("SWIATLO_KLATKA", 17, {activeLow = true})
local przycisk = GPIO_DIN:new("BTN_KLATKA", 27)

przycisk:add_event(DIN.EVENT_ON_CLICK, function()
    swiatlo:execute(DOUT.METHOD_SWITCH_ON, 60000)  -- 60 sekund
end)

Sterowanie obiektem z OM#

-- DOUT z fizycznego modulu Grenton
local lampa = _:get("CLU.DOUT1")

-- Te same metody co GPIO_DOUT
lampa:execute(DOUT.METHOD_SWITCH_ON)
lampa:add_event(DOUT.EVENT_ON_SWITCH_ON, function()
    log.info("Lampa wlaczona")
end)

expose(lampa, "switch", {name = "Lampa kuchnia"})

Kaskada - wlaczanie po kolei#

local r1 = GPIO_DOUT:new("R1", 17, {activeLow = true})
local r2 = GPIO_DOUT:new("R2", 22, {activeLow = true})
local r3 = GPIO_DOUT:new("R3", 24, {activeLow = true})

scene("wszystkie_on", function()
    r1:execute(DOUT.METHOD_SWITCH_ON)
    after(500, function() r2:execute(DOUT.METHOD_SWITCH_ON) end)
    after(1000, function() r3:execute(DOUT.METHOD_SWITCH_ON) end)
end)

after() dziala asynchronicznie - rejestruje timer i wraca natychmiast. Scena konczy sie w ulamku milisekundy, a callbacki odpalaja sie pozniej z petli timerowej Go. Nie ma ryzyka zawieszenia CLU.

after(500, ...)   ← NIE czeka 500ms, tylko rejestruje callback i idzie dalej
after(1000, ...)  ← to samo - rejestruje i wraca
-- scena konczy sie tu, natychmiast
-- po 500ms: r2 wlacza sie
-- po 1000ms: r3 wlacza sie
Lua w vCLU jest jednowatkowy - **nigdy nie uzywaj petli oczekujacej** (`while`, `repeat`, busy-wait). Zablokuje to caly runtime i zatrzyma przetwarzanie zdarzen, timerow, MQTT i GPIO. Do opoznien zawsze uzywaj `after()` lub `every()`. ```lua -- ZLE - zawiesi CLU! local start = os.clock() while os.clock() - start < 0.5 do end -- busy-wait r2:execute(DOUT.METHOD_SWITCH_ON) -- DOBRZE - asynchronicznie after(500, function() r2:execute(DOUT.METHOD_SWITCH_ON) end) ```

Przyklady mieszane - GPIO + lokalne moduly#

Mozna laczyc rozne warianty - np. fizyczny przycisk GPIO steruje wirtualnym DOUT, albo Tasmota przez MQTT steruje lokalnym modulem.

GPIO_DIN przelacza lokalny DOUT#

Fizyczny przycisk podlaczony do pinu GPIO steruje wirtualnym wyjsciem z wizarda:

-- user.lua

-- Przycisk fizyczny na GPIO
local btn = GPIO_DIN:new("BTN1", 27)

-- Lokalny DOUT z wizarda (ID nadany automatycznie)
local lampa = _:get("CLU.DOU5048")

btn:add_event(DIN.EVENT_ON_CLICK, function()
    lampa:execute(DOUT.METHOD_SWITCH)
end)

-- Eksponuj lokalny DOUT do Home Assistant
expose(lampa, "switch", {name = "Lampa salon"})

GPIO_DIN - krotkie/dlugie nacisniecie na lokalnych DOUT#

local btn = GPIO_DIN:new("BTN1", 27)

local lampa1 = _:get("CLU.DOU5048")
local lampa2 = _:get("CLU.DOU4458")

-- Krotkie nacisniecie - przelacz lampe 1
btn:add_event(DIN.EVENT_ON_SHORT_PRESS, function()
    lampa1:execute(DOUT.METHOD_SWITCH)
end)

-- Dlugie nacisniecie - wylacz obie lampy
btn:add_event(DIN.EVENT_ON_LONG_PRESS, function()
    lampa1:execute(DOUT.METHOD_SWITCH_OFF)
    lampa2:execute(DOUT.METHOD_SWITCH_OFF)
end)

Tasmota steruje lokalnym DOUT przez MQTT#

Gniazdko Tasmota podlaczone do wbudowanego brokera MQTT. Stan gniazdka synchronizowany z lokalnym DOUT - dzieki temu obiekt jest widoczny w Home Assistant i HomeKit jak kazdy inny DOUT:

-- user.lua

local mqtt = MQTT:new("tasmota")
mqtt:setHost("localhost"):setPort(1883):connect()

local gniazdko = _:get("CLU.DOU5048")

-- Tasmota → lokalny DOUT (synchronizacja stanu)
mqtt:subscribe("stat/gniazdko1/POWER", function(topic, payload)
    if payload == "ON" then
        gniazdko:set(DOUT.FEATURE_VALUE, 1)
    else
        gniazdko:set(DOUT.FEATURE_VALUE, 0)
    end
end)

-- Lokalny DOUT → Tasmota (przekazywanie komend)
gniazdko:add_event(DOUT.EVENT_ON_SWITCH_ON, function()
    mqtt:publish("cmnd/gniazdko1/POWER", "ON")
end)

gniazdko:add_event(DOUT.EVENT_ON_SWITCH_OFF, function()
    mqtt:publish("cmnd/gniazdko1/POWER", "OFF")
end)

-- Eksponuj do HA/HomeKit - sterowanie przez vCLU
expose(gniazdko, "switch", {name = "Gniazdko kuchnia"})

Przeplyw:

Home Assistant / HomeKit
        │
        ▼ (komenda ON)
   Lokalny DOUT
        │
        ├─▶ EVENT_ON_SWITCH_ON
        │         │
        │         ▼
        │   mqtt:publish("cmnd/gniazdko1/POWER", "ON")
        │         │
        │         ▼
        │   Tasmota wlacza gniazdko
        │         │
        │         ▼
        │   Tasmota → "stat/gniazdko1/POWER" = "ON"
        │         │
        │         ▼
        └── gniazdko:set(DOUT.FEATURE_VALUE, 1)  ← stan zsynchronizowany

Shelly jako czujnik temperatury + lokalny DOUT#

Shelly H&T publikuje temperature na MQTT. Gdy temperatura przekroczy prog - wlacz wentylator (lokalny DOUT):

local mqtt = MQTT:new("shelly")
mqtt:setHost("localhost"):setPort(1883):connect()

local wentylator = _:get("CLU.DOU4458")
expose(wentylator, "switch", {name = "Wentylator"})

mqtt:subscribe("shellies/shelly-ht/sensor/temperature", function(topic, payload)
    local temp = tonumber(payload)
    if temp and temp > 25 then
        wentylator:execute(DOUT.METHOD_SWITCH_ON)
    elseif temp and temp < 23 then
        wentylator:execute(DOUT.METHOD_SWITCH_OFF)
    end
end)

Wiele Tasmot - fabryka DOUT#

Jesli masz kilka gniazdek Tasmota, mozesz je podpiac pod lokalne DOUT za pomoca petli:

local mqtt = MQTT:new("tasmota")
mqtt:setHost("localhost"):setPort(1883):connect()

-- Mapowanie: nazwa Tasmota → ID lokalnego DOUT
local urzadzenia = {
    {tasmota = "gniazdko1", doutId = "CLU.DOU5048", name = "Gniazdko kuchnia"},
    {tasmota = "gniazdko2", doutId = "CLU.DOU4458", name = "Gniazdko salon"},
    {tasmota = "lampka1",   doutId = "CLU.DOU3327", name = "Lampka biurko"},
}

for _, dev in ipairs(urzadzenia) do
    local obj = _:get(dev.doutId)

    -- Tasmota → DOUT
    mqtt:subscribe("stat/" .. dev.tasmota .. "/POWER", function(topic, payload)
        if payload == "ON" then
            obj:set(DOUT.FEATURE_VALUE, 1)
        else
            obj:set(DOUT.FEATURE_VALUE, 0)
        end
    end)

    -- DOUT → Tasmota
    obj:add_event(DOUT.EVENT_ON_SWITCH_ON, function()
        mqtt:publish("cmnd/" .. dev.tasmota .. "/POWER", "ON")
    end)
    obj:add_event(DOUT.EVENT_ON_SWITCH_OFF, function()
        mqtt:publish("cmnd/" .. dev.tasmota .. "/POWER", "OFF")
    end)

    expose(obj, "switch", {name = dev.name})
end
Podejscie "lokalny DOUT + MQTT" pozwala traktowac urzadzenia Tasmota/Shelly jak natywne obiekty Grenton. Dzieki temu pojawiaja sie w Home Assistant i HomeKit, moga byc uzywane w scenach, i maja pelne zdarzenia (`add_event`).