Plugin API v2#
Kompletne API dostępne wewnątrz pluginu.
Tworzenie instancji#
local plugin = Plugin:new("my-plugin", {
name = "My Plugin",
version = "1.0.0",
description = "Opis pluginu"
})Lifecycle#
plugin:onInit(function(config)
-- Wywoływana po załadowaniu z obiektem config
end)
plugin:onCleanup(function()
-- Wywoływana przed wyładowaniem
-- Timery, eventy, MQTT auto-czyszczone przez sandbox
end)HTTP#
httpRequest#
plugin:httpRequest({
url = "https://api.example.com/data",
method = "GET", -- GET|POST|PUT|DELETE
headers = {["X-Key"] = "value"},
body = "raw body", -- lub:
json = {key = "value"}, -- auto JSON.encode
form = {field = "value"}, -- form-urlencoded
timeout = 10000, -- ms
parseJson = "success", -- "success"|"always"|"never"
log = {redact = true}, -- ukryj sekrety w logach
}, function(resp)
-- resp.status — HTTP status code
-- resp.body — raw body string
-- resp.headers — response headers
-- resp.json — parsed JSON (jeśli parseJson)
-- resp.err — error string lub nil
-- resp.elapsed — czas odpowiedzi ms
-- resp.ok — true jeśli 2xx
end)URL builder#
local url = plugin:url("https://api.example.com/data", {
apiKey = "secret",
city = "Warsaw",
format = "json"
})
-- → https://api.example.com/data?apiKey=secret&city=Warsaw&format=jsonParametry sortowane alfabetycznie, wartości URL-encoded.
Auth helpers#
plugin:basicAuth("user", "pass") -- → "Basic dXNlcjpwYXNz"
plugin:bearerAuth("token") -- → "Bearer token"Poller#
Zarządzana pętla HTTP z retry i backoff:
local poller = plugin:poller("fetch", {
interval = 60000, -- ms między tickami
immediate = true, -- start od razu
initialDelay = 1000, -- opóźnienie pierwszego ticku
timeout = 30000, -- timeout ticku
retry = {
maxAttempts = 3,
backoff = 2000, -- ms między retry
},
onTick = function(done)
plugin:httpRequest({
url = plugin:url(baseUrl, params),
parseJson = "success"
}, function(resp)
if resp.err then
done(resp.err) -- trigger retry
return
end
-- przetwarzanie danych...
done() -- sukces
end)
end,
onError = function(err, stats)
plugin:log("error", "Failed: " .. err)
end
})
poller:start()
poller:stop()
poller:poll() -- wymuś natychmiast
poller:stats() -- statystyki (ticks, errors, lastTick)Registry (obiekty pluginu)#
Obiekty zapisywane w plugins.{namespace}.{shortId}.{path}:
-- Utwórz lub nadpisz
plugin:upsertObject("current", {
ready = false,
value = 0,
lastUpdate = 0
})
-- Aktualizuj (shallow merge, wymaga istnienia)
plugin:updateObject("current", {
ready = true,
value = 22.5,
lastUpdate = os.time()
})
-- Odczyt (własne lub pełna ścieżka)
local obj = plugin:getObject("current")
local other = plugin:getObject("plugins.vclu.weather.current")updateObject waliduje nazwy pól i sugeruje poprawki przy literówkach.
Events#
-- Emisja
plugin:emit("weather:changed", {temp = 22.5}, {
throttle = 60000 -- max 1 event na 60s
})
-- Emisja z trailing edge
plugin:emit("position:updated", data, {
throttle = 1000,
trailing = true -- emituj ostatnią wartość po oknie
})
-- Coalesce (zbieranie)
plugin:emit("batch:item", {item = "a"}, {
throttle = 5000,
coalesce = true,
key = "item" -- wynik: {items = ["a", "b", "c"]}
})
-- Subskrypcja
plugin:on("telegram:message", function(data)
plugin:log("info", "Message: " .. data.text)
end)Timery#
local id = plugin:setTimeout(5000, function()
plugin:log("info", "Jednorazowy timer")
end)
local id = plugin:setInterval(10000, function()
plugin:log("info", "Co 10 sekund")
end)
plugin:clearTimer(id)Auto-czyszczone przy unload pluginu.
MQTT#
-- Publikacja
plugin:mqttPublish("sensors/temperature", "22.5", {
retain = true,
qos = 1
})
-- Subskrypcja
plugin:mqttSubscribe("commands/#", function(topic, payload)
plugin:log("info", topic .. ": " .. payload)
end)Sensor / Control (Expose API)#
-- Read-only sensor
plugin:sensor("temperature", function()
return state.temperature
end)
-- Read-write control
plugin:control("fanSpeed",
function() return state.fanSpeed end, -- getter
function(level) -- setter
state.fanSpeed = level
-- wysłanie komendy do urządzenia...
end
)
-- Pobranie sensora/kontrolki
local sensor = plugin:get("temperature")
-- Powiadomienie expose o zmianie wartości
if sensor then sensor:notify() endSensory i kontrolki integrują się z expose():
expose(plugin:get("temperature"), "temperature", {
name = "Temp zewnętrzna",
unit = "°C"
})
expose(plugin:get("fanSpeed"), "fan", {
name = "Wentylator",
min = 0, max = 4
})KV Store (persystencja)#
Dane przetrwają restart. Izolacja per-plugin.
-- Zapis
plugin:kvSet("last_offset", 12345)
-- Zapis z opcjami
plugin:kvSet("oauth_token", token, {
secure = true, -- ukryte w logach/UI
ttl = 3600 -- wygasa po 1h
})
-- Odczyt
local offset = plugin:kvGet("last_offset", 0) -- default = 0
-- Inne operacje
plugin:kvHas("key") -- → boolean
plugin:kvDelete("key")
plugin:kvList("cache:") -- → klucze z prefiksem
plugin:kvGetAll() -- → {key → value}Przechowywane w kv/{namespace}--{shortId}.json.
Type coercion#
Bezpieczna konwersja typów z config:
local interval = plugin:coerceNumber(config.interval, 3600)
local name = plugin:coerceString(config.name, "default")
local enabled = plugin:coerceBool(config.enabled, true)Logging#
plugin:log("info", "Data fetched")
plugin:log("error", "Connection failed")
plugin:log("debug", "Raw response: " .. body)
plugin:log("warn", "Rate limit approaching")
-- Z redakcją sekretów
plugin:logSafe("info", "API call", {
url = "https://api.example.com",
apiKey = "sk-1234567890" -- → "sk-12***"
}, true)Redaktowane pola: apiKey, token, password, secret, auth, bearer.
Dostęp do innych pluginów#
-- Po full ID
local weather = Plugin.getPlugin("@vclu/weather")
-- Po short ID (jeśli unikalny)
local w = Plugin.getPlugin("weather")
if weather and weather:isReady() then
local temp = weather:getTemperature()
end
-- Lista wszystkich pluginów
for _, p in ipairs(Plugin.list()) do
plugin:log("info", p.name .. " v" .. p.version)
endExpose do Home Assistant/HomeKit#
-- W onInit:
plugin:sensor("temperature", function() return state.temp end)
plugin:sensor("humidity", function() return state.humidity end)
plugin:control("fanSpeed",
function() return state.fanSpeed end,
function(v) state.fanSpeed = v end
)
-- W user.lua:
local salda = Plugin.getPlugin("@vclu/salda-recuperator")
if salda then
expose(salda:get("supplyAir"), "temperature", {name = "Nawiew", unit = "°C"})
expose(salda:get("humidity"), "humidity", {name = "Wilgotność"})
expose(salda:get("fanSpeed"), "fan", {name = "Wentylator", min = 0, max = 4})
end