MQTT#
vCLU obsługuje MQTT na dwa sposoby:
- Klient MQTT - vCLU łączy się z zewnętrznym brokerem (np. Mosquitto) i publikuje/odbiera wiadomości
- Wbudowany broker MQTT - vCLU sam jest brokerem, inne urządzenia (Tasmota, Shelly, ESPHome) łączą się do niego
Oba tryby mogą działać jednocześnie.
Architektura#
Urządzenia zewnętrzne vCLU
(Tasmota, Shelly...) ┌──────────────────────┐
│ │ │
│ TCP :1883 │ Wbudowany broker │
└───────────────────────────▶│ (mochi-mqtt) │
│ ▲ │
│ │ localhost │
│ ┌────┴─────────┐ │
Zewnętrzny broker │ │ Klient MQTT │ │
(Mosquitto, EMQX...) │ │ (paho) │ │
▲ │ └──────────────┘ │
│ TCP │ │
└────────────────────────────┤ Lua runtime │
└──────────────────────┘Klient MQTT - vCLU jako klient#
Tworzenie i konfiguracja#
-- Utwórz nową instancję klienta
local mqtt = MQTT:new("moj_klient")
-- Konfiguracja brokera
mqtt:setHost("192.168.1.100") -- adres brokera (automatycznie dodaje tcp://)
mqtt:setPort(1883) -- port (domyślnie 1883)
mqtt:setClientId("vclu_salon") -- unikalna nazwa klienta
-- Opcjonalna autoryzacja
mqtt:setCredentials("user", "haslo")
-- Opcjonalne ustawienia
mqtt:setKeepAlive(60) -- keep-alive w sekundach
mqtt:setAutoReconnect(true, 5000) -- auto-reconnect co 5s
mqtt:setTopicPrefix("dom/vclu") -- prefix dodawany do każdego topica
mqtt:setQoS(0) -- domyślny QoS (0, 1 lub 2)Metody konfiguracyjne wspierają chaining:
local mqtt = MQTT:new("klient1")
mqtt:setHost("192.168.1.100")
:setPort(1883)
:setClientId("vclu_1")
:setCredentials("user", "pass")Połączenie#
if mqtt:connect() then
print("Połączono z brokerem!")
else
print("Nie udało się połączyć")
end
-- Sprawdź status
print(mqtt:isConnected()) -- true/falseWysyłanie wiadomości (publish)#
-- Podstawowe wysłanie
mqtt:publish("salon/lampa/status", "ON")
-- Z parametrami QoS i retain
mqtt:publish("salon/temperatura", "22.5", 1, true)
-- QoS retain
-- Tabela Lua jest automatycznie zamieniana na JSON
mqtt:publish("salon/czujnik", {
temperature = 22.5,
humidity = 45,
battery = 98
})
-- Wysyła: {"temperature":22.5,"humidity":45,"battery":98}Jeśli ustawiono `setTopicPrefix("dom/vclu")`, topic `"salon/lampa"` zostanie wysłany jako `"dom/vclu/salon/lampa"`.
Odbieranie wiadomości (subscribe)#
-- Subskrybuj topic z callbackiem
mqtt:subscribe("salon/przycisk/akcja", function(topic, payload, qos, retain)
print("Odebrano: " .. topic .. " = " .. payload)
if payload == "ON" then
_:get("CLU.DOUT1"):execute(DOUT.METHOD_SWITCH_ON)
elseif payload == "OFF" then
_:get("CLU.DOUT1"):execute(DOUT.METHOD_SWITCH_OFF)
end
end)
-- Wildcard "+" - jeden poziom
mqtt:subscribe("dom/+/temperatura", function(topic, payload)
-- Dopasuje: dom/salon/temperatura, dom/kuchnia/temperatura
print(topic .. " = " .. payload)
end)
-- Wildcard "#" - zero lub więcej poziomów (tylko na końcu)
mqtt:subscribe("dom/#", function(topic, payload)
-- Dopasuje: dom/salon/lampa, dom/kuchnia/temp, dom/x/y/z
print(topic .. " = " .. payload)
end)
-- Rezygnacja z subskrypcji
mqtt:unsubscribe("salon/przycisk/akcja")Eventy#
mqtt:onConnect(function()
print("Połączono!")
-- Typowe: po połączeniu odnów subskrypcje
mqtt:subscribe("dom/#", handler)
end)
mqtt:onDisconnect(function()
print("Rozłączono!")
end)
-- Globalny handler - otrzymuje WSZYSTKIE wiadomości (niezależnie od subscribe)
mqtt:onMessage(function(topic, payload, qos, retain)
log.debug("MQTT: " .. topic .. " = " .. payload)
end)
mqtt:onError(function(err)
log.error("MQTT błąd: " .. tostring(err))
end)Rozłączenie#
mqtt:disconnect()Wbudowany broker MQTT - vCLU jako serwer#
Wbudowany broker pozwala urządzeniom IoT (Tasmota, Shelly, ESPHome) łączyć się bezpośrednio z vCLU bez zewnętrznego brokera.
Konfiguracja#
Broker konfiguruje się w panelu webowym (/mqtt) lub przez API:
curl -X POST http://vclu:3000/api/mqtt/broker/save \
-H "Content-Type: application/json" \
-d '{
"enabled": true,
"port": 1883,
"wsPort": 0,
"username": "mqtt",
"password": "tajne"
}'| Parametr | Opis | Domyślnie |
|---|---|---|
enabled | Włącz broker | false |
port | Port TCP | 1883 |
wsPort | Port WebSocket (0 = wyłączony) | 0 |
username | Nazwa użytkownika (pusty = bez autoryzacji) | "" |
password | Hasło | "" |
Konfiguracja zapisywana jest w .vclu.json:
{
"mqttBroker": {
"enabled": true,
"port": 1883,
"username": "mqtt",
"password": "tajne"
}
}Łączenie z wbudowanym brokerem z Lua#
Aby odebrać wiadomości od urządzeń podłączonych do wbudowanego brokera, utwórz klienta MQTT wskazującego na localhost:
local mqtt = MQTT:new("lokalny")
mqtt:setHost("localhost")
mqtt:setPort(1883)
mqtt:setCredentials("mqtt", "tajne") -- te same dane co w konfiguracji brokera
mqtt:connect()Praktyczne przykłady#
Tasmota - sterowanie przekaźnikiem#
Tasmota publikuje stan na stat/<nazwa>/POWER i przyjmuje komendy na cmnd/<nazwa>/POWER.
local mqtt = MQTT:new("tasmota")
mqtt:setHost("localhost") -- wbudowany broker
mqtt:setPort(1883)
mqtt:setCredentials("mqtt", "tajne")
mqtt:connect()
-- Odbierz stan z Tasmota
mqtt:subscribe("stat/gniazdko1/POWER", function(topic, payload)
if payload == "ON" then
log.info("Gniazdko włączone")
else
log.info("Gniazdko wyłączone")
end
end)
-- Wyślij komendę do Tasmota
mqtt:publish("cmnd/gniazdko1/POWER", "TOGGLE") -- przełącz
mqtt:publish("cmnd/gniazdko1/POWER", "ON") -- włącz
mqtt:publish("cmnd/gniazdko1/POWER", "OFF") -- wyłączShelly - odczyt temperatury#
local mqtt = MQTT:new("shelly")
mqtt:setHost("localhost")
mqtt:setPort(1883)
mqtt:connect()
mqtt:subscribe("shellies/shelly-ht/sensor/temperature", function(topic, payload)
local temp = tonumber(payload)
log.info("Temperatura: " .. temp .. "°C")
-- Reaguj na temperaturę
if temp > 25 then
_:get("CLU.DOUT2"):execute(DOUT.METHOD_SWITCH_ON) -- włącz wentylator
end
end)Mostek między dwoma brokerami#
vCLU jako klient zewnętrznego brokera + wbudowany broker dla urządzeń lokalnych:
-- Klient do centralnego brokera w sieci (np. Mosquitto na NAS)
local central = MQTT:new("central")
central:setHost("192.168.1.50")
central:setPort(1883)
central:connect()
-- Klient do lokalnego wbudowanego brokera (urządzenia Tasmota/Shelly)
local local_mqtt = MQTT:new("local")
local_mqtt:setHost("localhost")
local_mqtt:setPort(1883)
local_mqtt:connect()
-- Lokalne urządzenia → centralny broker
-- Stany z Tasmota/Shelly trafiają do centralnego brokera z prefixem "vclu/"
local_mqtt:subscribe("stat/#", function(topic, payload)
central:publish("vclu/" .. topic, payload)
end)
-- Centralny broker → lokalne urządzenia
-- Komendy wysłane na centralny broker trafiają do lokalnych urządzeń
central:subscribe("vclu/cmnd/#", function(topic, payload)
-- Usuń prefix "vclu/" i przekaż do lokalnego brokera
local localTopic = topic:gsub("^vclu/", "")
local_mqtt:publish(localTopic, payload)
end)Dzięki temu np. Node-RED podłączony do centralnego Mosquitto może sterować Tasmotą podłączoną do wbudowanego brokera vCLU:
Node-RED → Mosquitto → vCLU (central) → vCLU (local_mqtt) → Tasmota
"vclu/cmnd/gniazdko1/POWER" "cmnd/gniazdko1/POWER"Przycisk Grenton → MQTT#
local mqtt = MQTT:new("eventy")
mqtt:setHost("localhost"):setPort(1883):connect()
-- Gdy przycisk DIN1 zostanie naciśnięty, wyślij na MQTT
_:get("CLU.DIN1"):add_event(DIN.EVENT_ON_SWITCH_ON, function()
mqtt:publish("dom/salon/przycisk", "pressed", 0, false)
end)MQTT → sterowanie sceną#
local mqtt = MQTT:new("sceny")
mqtt:setHost("localhost"):setPort(1883):connect()
mqtt:subscribe("dom/scena/uruchom", function(topic, payload)
-- payload = nazwa sceny, np. "wieczor"
runScene(payload)
end)Panel webowy#
Strona /mqtt pozwala:
- Włączyć/wyłączyć i skonfigurować wbudowany broker
- Zobaczyć podłączonych klientów (urządzenia)
- Zobaczyć instancje klienta MQTT z Lua
- Wysłać testową wiadomość
- Opublikować Home Assistant Discovery (patrz Home Assistant)
Jak działa pod spodem#
Lua: mqtt:publish("topic", "payload")
↓
Go: __go_mqtt_publish() → MQTTManager.Publish()
↓
Paho MQTT Client → broker (zewnętrzny lub wbudowany)Broker odbiera wiadomość od urządzenia
↓
Paho Client → wiadomość trafia do kanału Go (bufor 1000)
↓
Lua: MQTT.pollMessages() (wywoływane co 100ms)
↓
__go_mqtt_poll() pobiera wiadomość z kanału
↓
Callback z mqtt:subscribe() jest wywołany