Zdalne CLU#
vCLU moze sterowac obiektami na zdalnych CLU (fizycznych Grenton lub innych vCLU) tak, jakby byly lokalne. Komunikacja odbywa sie przez szyfrowane pakiety UDP.
Architektura#
┌─────────────────┐ UDP (AES-128 CBC) ┌─────────────────┐
│ vCLU (lokalne) │ ──────────────────────────────── │ CLU (zdalne) │
│ │ port 1234 │ │
│ dom.LAMPA:on() │ ──req:IP:SESSION:script──────────▶│ LAMPA:on() │
│ │ ◀─resp:IP:SESSION:result────────── │ │
│ │ │ │
│ │ port 4344 │ │
│ registry update │ ◀─clientReport:SID:{values}─────── │ push changes │
└─────────────────┘ └─────────────────┘Dwa tryby komunikacji:
| Tryb | Kierunek | Opis |
|---|---|---|
| Komendy | lokalne -> zdalne | Wysylanie skryptow Lua, oczekiwanie na wynik |
| Push reports | zdalne -> lokalne | Powiadomienia o zmianach stanow (subskrypcje) |
Import pliku om.lua#
Aby sterowac zdalnym CLU, trzeba zaimportowac jego plik om.lua. Plik ten zawiera definicje wszystkich obiektow (moduly, I/O, nazwy).
Skad wziac plik om.lua#
- Z Object Managera - eksport diagnostyczny (ZIP z plikami om.lua kazdego CLU)
- Z fizycznego CLU - plik przesylany przez TFTP podczas konfiguracji OM
- Z innego vCLU - plik
om.luaz katalogu danych
Upload przez interfejs webowy#
Strona OM Files (/om-files) pozwala zaimportowac pliki:
- Wejdz na
/om-files - Kliknij “Upload” i wybierz plik
.lualub.zip - vCLU automatycznie parsuje plik i generuje proxy
Obslugiwane formaty:
- Plik .lua - pojedynczy plik om.lua jednego CLU
- Plik .zip - paczka diagnostyczna OM (zawiera pliki om.lua wielu CLU)
Co sie dzieje po imporcie#
Upload om_221000290.lua
│
▼
┌──────────┐
│ Parser │ ← Wyodrebnia: serial, IP, moduly, obiekty, nazwy
└────┬─────┘
│
▼
┌────────────────┐
│ Generator │ ← Tworzy proxy_221000290.lua
│ proxy │ (CLU_REMOTE + RemoteSwitch/RemoteSensor/...)
└────┬───────────┘
│
▼
┌────────────────┐
│ Lua runtime │ ← Laduje proxy → obiekty w _registry
│ LoadProxyFiles │ dom.LAMPA = RemoteSwitch:new(...)
└────────────────┘Pliki zapisywane na dysku:
imports/
om_221000290.lua ← oryginalny plik OM
proxy_221000290.lua ← wygenerowany proxy
proxy_local.lua ← rejestracja lokalnych obiektowFormat pliku om.lua#
Plik om.lua generowany przez Object Manager zawiera:
-- FwType 00000003
-- FwVersion 000001f6
CLU221000290 = OBJECT:new(0, 0xC0A80003)
-- NAME_CLU DOM20=CLU221000290
-- Moduly fizyczne
mm_330000027 = OBJECT:new(2, 0x13ab669b, 0x1e, "13ab669b")
-- Obiekty I/O
DOU5048 = OBJECT:new(4, mm_330000027, 0)
-- NAME_IO LAMPA_SALON=DOU5048
DIN1473 = OBJECT:new(3, mm_330000028, 1)
-- NAME_IO CZUJNIK_RUCHU=DIN1473
-- Event handlery
DIN1473:add_event(2, EventsFor_DIN1473_2)Wygenerowany proxy#
Z powyzszego pliku OM, generator tworzy:
-- Proxy generated from CLU221000290 (DOM20)
-- IP: 192.168.0.3
-- Referencja do zdalnego CLU
CLU221000290 = CLU_REMOTE:new(221000290, 0xC0A80003, system:getEventBus())
local _clu = CLU221000290
-- Namespace
dom20 = {}
-- DOUT (1 objects)
dom20.LAMPA_SALON = RemoteSwitch:new(_clu, "DOU5048", "LAMPA_SALON")
-- DIN (1 objects)
dom20.CZUJNIK_RUCHU = RemoteSensor:new(_clu, "DIN1473", "CZUJNIK_RUCHU")
-- Rejestracja w _registry
registerObject("CLU221000290.DOU5048", dom20.LAMPA_SALON)
registerObject("CLU221000290.DIN1473", dom20.CZUJNIK_RUCHU)Obiekty proxy#
Po imporcie om.lua, obiekty zdalne dostepne sa jako zmienne globalne w user.lua:
-- Przez namespace (nazwa CLU)
dom20.LAMPA_SALON:execute(DOUT.METHOD_SWITCH_ON)
dom20.CZUJNIK_RUCHU:get(DIN.FEATURE_VALUE)
-- Przez registry (sciezka CLU.ObjID)
_:get("CLU221000290.DOU5048"):execute(DOUT.METHOD_SWITCH_ON)Typy obiektow proxy#
| Klasa OM | Proxy | Opis |
|---|---|---|
| DOUT | RemoteSwitch | Przelacznik (on/off/toggle) |
| DIN | RemoteSensor | Czujnik binarny (odczyt) |
| DIMMER | RemoteLight | Scialniacz (jasnosc) |
| LEDRGB | RemoteRGBLight | LED RGB |
| ROLLER_SH, ROLLER | RemoteCover | Roleta |
| AnalogIN | RemoteSensor | Wejscie analogowe (odczyt) |
| AnalogOUT | RemoteNumber | Wyjscie analogowe (zapis) |
RemoteObject - bazowa klasa#
Wszystkie obiekty proxy dziedzicza z RemoteObject. Kazde wywolanie get(), set(), execute() jest przekazywane do zdalnego CLU:
-- Te wywolania:
dom20.LAMPA:get(0)
dom20.LAMPA:set(0, 1)
dom20.LAMPA:execute(1)
-- Generuja skrypty Lua wysylane na zdalne CLU:
-- "DOU5048:get(0)"
-- "DOU5048:set(0, 1)"
-- "DOU5048:execute(1)"Dodatkowe metody:
-- Tagi
dom20.LAMPA:addTag("lights", "salon")
dom20.LAMPA:hasTag("lights") -- true
dom20.LAMPA:getTags() -- {"lights", "salon"}
-- Zdarzenia (lokalne)
dom20.LAMPA:on("OnChange", function(newState, oldState)
log.info("LAMPA zmienila stan: %s -> %s", oldState, newState)
end)
dom20.LAMPA:onChange(function(newState, oldState)
-- skrot dla on("OnChange", ...)
end)
-- Informacje
dom20.LAMPA:getInfo() -- {id, name, type, tags, methods}
dom20.LAMPA:toString() -- sformatowany string do debugowaniaProtokol komunikacji#
Komendy (request/response)#
Format pakietow UDP (port 1234):
Request: req:<IP>:<SESSION_HEX>:<LUA_SCRIPT>\r\n
Response: resp:<IP>:<SESSION_HEX>:<RESULT>Przyklad:
→ req:192.168.0.1:0000A1B2:DOU5048:get(0)\r\n
← resp:192.168.0.3:0000A1B2:1Parametry:
| Parametr | Wartosc |
|---|---|
| Szyfrowanie | AES-128 CBC (klucz projektu + IV) |
| Port | 1234 (CommandPort) |
| Timeout | 5 sekund |
| Sesja | Losowy uint32 per request |
Subskrypcje (push notifications)#
Zdalne CLU moze powiadamiac o zmianach stanow obiektow. Uzywane przez RemoteSubscription do aktualizacji rejestru bez odpytywania.
-- Auto-subscribe odbywa sie przy starcie vCLU
-- Skanuje _registry, grupuje obiekty po IP, tworzy subskrypcjeProtokol:
→ SYSTEM:clientRegister('192.168.0.1', 4344, 123, {{DOU5048, 0}})
← clientReport:123:{1}| Parametr | Wartosc |
|---|---|
| Port nasluchiwania | 4344 |
| Max obiektow per subskrypcja | 4 |
| TTL subskrypcji | 60 sekund (auto-refresh) |
Wieksze zestawy obiektow sa automatycznie dzielone na partie po 4.
Szyfrowanie#
Oba tryby komunikacji uzywaja tego samego szyfrowania AES-128 CBC:
- Klucz generowany z
projectKeyiIV(XOR pierwszej/drugiej polowy) - Dane szyfrowane przed wyslaniem
- Odbiorca deszyfruje odpowiedz tym samym kluczem
Klucze konfigurowane w wizardzie (krok 3 - Keys & PIN).
Synchronizacja stanu#
Zdalne obiekty moga aktualizowac swoj stan w registry vCLU na dwa sposoby:
Push reports (subskrypcje)#
Glowny mechanizm. Po starcie vCLU automatycznie subskrybuje obiekty na zdalnych CLU (patrz sekcja Subskrypcje wyzej). Gdy stan obiektu zmieni sie na zdalnym CLU, przychodzi clientReport i vCLU aktualizuje _lastState obiektu w registry.
sync() - wywolanie z zewnetrznego CLU#
Zdalne CLU moze jawnie zaktualizowac stan obiektu w vCLU. W konfiguracji OM na fizycznym CLU dodaje sie event handler, ktory przy zmianie stanu wywoluje:
-- Konfiguracja na fizycznym CLU (w Object Managerze):
-- OnSwitchOn → VCLU:execute("_:sync('CLU221000290.DOU5048', 1)")
-- OnSwitchOff → VCLU:execute("_:sync('CLU221000290.DOU5048', 0)")_:sync(sciezka, wartosc) na vCLU:
- Znajduje obiekt w registry (po sciezce lub nazwie)
- Aktualizuje
_lastState - Emituje
state_changedna StateBus - Integracje (MQTT, HomeKit, WebSocket) automatycznie otrzymuja zmiane
-- sync() akceptuje sciezke lub nazwe
_:sync("CLU221000290.DOU5048", 1) -- po sciezce
_:sync("LAMPA_SALON", 1) -- po nazwie
-- Stringi liczbowe sa konwertowane automatycznie
_:sync("CLU221000290.DOU5048", "1") -- wartosc staje sie liczba 1Autoexpose#
Po zaladowaniu proxy zdalnych obiektow, vCLU automatycznie wywoluje exposeRegistry() — wszystkie zaimportowane obiekty sa od razu widoczne w MQTT i HomeKit bez recznej konfiguracji w user.lua.
Przyklady#
Sterowanie zdalnymi lampami#
-- Wlacz wszystkie lampy w salonie (zdalne CLU "dom20")
dom20.LAMPA_SALON:execute(DOUT.METHOD_SWITCH_ON)
dom20.LAMPA_KUCHNIA:execute(DOUT.METHOD_SWITCH_ON)
-- Odczytaj stan
local stan = dom20.LAMPA_SALON:get(DOUT.FEATURE_VALUE)
log.info("Lampa salon: %d", stan)Scena z wieloma CLU#
-- Scena "wieczor" - obiekty z roznych CLU
scene("wieczor", function()
-- Lokalne (ten vCLU)
_:get("CLU.DOU5048"):execute(DOUT.METHOD_SWITCH_ON)
-- Zdalne (fizyczny CLU "dom20")
dom20.LAMPA_SALON:execute(DOUT.METHOD_SWITCH_ON)
dom20.LAMPA_KORYTARZ:execute(DOUT.METHOD_SWITCH_ON)
-- Zdalne (drugi vCLU "garage")
garage.BRAMA:execute(DOUT.METHOD_SWITCH_OFF)
end)Tagi i grupy mieszane#
-- Otaguj obiekty z roznych CLU
dom20.LAMPA_SALON:addTag("lights")
dom20.LAMPA_KUCHNIA:addTag("lights")
_:get("CLU.DOU5048"):addTag("lights")
-- Steruj wszystkimi naraz
local lights = _:byTag("lights")
lights:forEach(function(obj)
obj:execute(DOUT.METHOD_SWITCH_OFF)
end)Reagowanie na zmiany stanu zdalnego obiektu#
-- Callback gdy zmieni sie stan zdalnej lampy
dom20.LAMPA_SALON:onChange(function(newState, oldState)
log.info("Lampa salon: %s -> %s", tostring(oldState), tostring(newState))
-- Synchronizuj z lokalnym obiektem
if newState == 1 then
_:get("CLU.DOU5048"):execute(DOUT.METHOD_SWITCH_ON)
else
_:get("CLU.DOU5048"):execute(DOUT.METHOD_SWITCH_OFF)
end
end)RPC - wywolanie dowolnej funkcji na zdalnym CLU#
Obiekty proxy (RemoteSwitch, RemoteSensor, …) to wygodne wrappery, ale pod spodem kazde wywolanie to po prostu wyslanie skryptu Lua na zdalne CLU. Mozna to samo zrobic recznie przez CLU_REMOTE:execute():
-- CLU_REMOTE:execute(0, skrypt_lua) → wysyla skrypt i zwraca wynik
-- Wywolaj funkcje zdefiniowana w user.lua na zdalnym CLU
CLU221000290:execute(0, "mojaFunkcja()")
-- Wywolaj z argumentami
CLU221000290:execute(0, 'wlaczSwiatlo("salon", 80)')
-- Odczytaj wartosc zmiennej
local temp = CLU221000290:execute(0, "return THERM1:get(0)")
log.info("Temperatura: %s", tostring(temp))
-- Dowolny skrypt Lua — jeden wyrazenie
local count = CLU221000290:execute(0, "return #_:list()")Pierwszy argument to zawsze 0 (indeks metody execute na CLU). Drugi to string ze skryptem Lua, ktory zostanie wykonany na zdalnym CLU. Wynik jest zwracany jako string.
Przez namespace:
-- Jesli zdalne CLU ma namespace "dom20":
-- CLU221000290 jest dostepny jako zmienna globalna po imporcie om.lua
-- Wywolaj scene na zdalnym CLU
CLU221000290:execute(0, "runScene('wieczor')")
-- Zmien zmienna na zdalnym CLU
CLU221000290:execute(0, "setVar('tryb', 'eco')")
-- Odczytaj stan dowolnego obiektu
local val = CLU221000290:execute(0, "return DOUT3:get(0)")`execute()` zwraca wynik tylko z pojedynczego wyrazenia. Skrypty wieloliniowe dzialaja, ale aby zwrocic wartosc trzeba uzyc `return` na poczatku.
Expose zdalnych obiektow do MQTT / HomeKit#
-- Zdalne obiekty mozna eksponowac tak samo jak lokalne
expose(dom20.LAMPA_SALON, "switch", {name = "Lampa Salon"})
expose(dom20.CZUJNIK_RUCHU, "binary_sensor", {name = "Ruch Salon"})