Storage (KV Store)#

vCLU udostepnia persystentny key-value store dostepny z Lua. Dane przetrwaja restart silnika i sa zapisywane na dysku jako pliki JSON.

API z poziomu Lua#

kv:set(key, value, secure, ttl)#

Zapisz wartosc.

-- Podstawowy zapis
kv:set("sensor_threshold", 22)

-- Zapis stringa
kv:set("last_scene", "wieczor")

-- Zapis tablicy (automatyczna serializacja)
kv:set("config", {temp_min = 18, temp_max = 25})

kv:get(key)#

Odczytaj wartosc. Zwraca (value, exists).

local val, exists = kv:get("sensor_threshold")
if exists then
    log.info("Prog: " .. val)
end

-- Z wartoscia domyslna
local threshold = kv:get("sensor_threshold") or 22

kv:has(key)#

Sprawdz czy klucz istnieje (i nie wygasl).

if kv:has("api_token") then
    -- token jest dostepny
end

kv:delete(key)#

Usun klucz.

kv:delete("session_token")

kv:list(prefix)#

Lista kluczy z opcjonalnym prefixem.

local keys = kv:list("sensor_")
-- {"sensor_threshold", "sensor_interval", "sensor_last_read"}

local all = kv:list()  -- wszystkie klucze

kv:getAll()#

Pobierz wszystkie pary klucz-wartosc. Wartosci zaszyfrowane sa zastepowane przez "[SECURE]".

local all = kv:getAll()
for k, v in pairs(all) do
    log.info(k .. " = " .. tostring(v))
end

Szyfrowanie#

Wrazliwe dane (tokeny API, hasla) mozna zapisac zaszyfrowane:

-- Trzeci parametr = secure
kv:set("api_secret", "s3cret_token_123", true)

-- Odczyt dziala normalnie
local secret = kv:get("api_secret")  -- "s3cret_token_123"

-- Ale getAll() maskuje wartosc
local all = kv:getAll()
-- all.api_secret = "[SECURE]"

W logach wartosc tez jest ukryta:

[KV] Set myplugin.api_secret = [REDACTED] (secure, ttl=0)

TTL (Time-to-Live)#

Klucze moga wygasac automatycznie po zadanym czasie:

-- Czwarty parametr = TTL w sekundach
kv:set("session_token", token, false, 3600)    -- wygasnie po 1 godzinie
kv:set("cache_data", data, false, 300)          -- wygasnie po 5 minutach

-- Szyfrowane + z TTL
kv:set("oauth_token", token, true, 7200)        -- szyfrowane, wygasa po 2h

Po wygasnieciu klucz jest usuwany przy nastepnym odczycie (lazy expiration):

kv:set("temp", "wartosc", false, 5)  -- 5 sekund TTL

-- Po 5 sekundach:
local val, exists = kv:get("temp")
-- val = nil, exists = false

Scope - izolacja per plugin#

Kazdy plugin ma wlasny namespace w KV store. Pluginy nie widza kluczy innych pluginow:

-- W pluginie "weather":
kv:set("api_key", "abc123")     -- zapisane w pliku kv/weather.json
kv:get("api_key")               -- "abc123"

-- W pluginie "lights":
kv:get("api_key")               -- nil (nie widzi kluczy "weather")

Pliki na dysku:

kv/
  weather.json
  lights.json
  vclu--my-plugin.json    -- @vclu/my-plugin → vclu--my-plugin

Zapis na dysk#

Dane sa zapisywane atomowo (zapis do pliku tymczasowego, potem rename) przy kazdej operacji set() i delete(). Format pliku:

{
  "_meta": {
    "version": 1,
    "updated": 1708617600
  },
  "entries": {
    "sensor_threshold": {
      "v": 22,
      "created": 1708617500
    },
    "api_secret": {
      "v": "zaszyfrowana_wartosc",
      "secure": true,
      "created": 1708617550
    },
    "cache": {
      "v": "dane",
      "ttl": 300,
      "exp": 1708617900,
      "created": 1708617600
    }
  }
}

Przyklady#

Zapamietanie ostatniej sceny#

scene("wieczor", function()
    _:get("CLU.DOU5048"):execute(DOUT.METHOD_SWITCH_ON)
    _:get("CLU.DOU4458"):execute(DOUT.METHOD_SWITCH_OFF)
    kv:set("last_scene", "wieczor")
end)

scene("poranek", function()
    _:get("CLU.DOU5048"):execute(DOUT.METHOD_SWITCH_OFF)
    _:get("CLU.DOU4458"):execute(DOUT.METHOD_SWITCH_ON)
    kv:set("last_scene", "poranek")
end)

-- Po restarcie - przywroc ostatnia scene
local last = kv:get("last_scene")
if last then
    runScene(last)
end

Licznik wlaczen#

_:get("CLU.DOU5048"):add_event(DOUT.EVENT_ON_SWITCH_ON, function()
    local count = kv:get("relay1_count") or 0
    kv:set("relay1_count", count + 1)
end)

Cache z automatycznym wygasaniem#

local function getWeather()
    local cached = kv:get("weather_data")
    if cached then
        return cached  -- zwroc z cache
    end

    -- Pobierz z API
    http:get("https://api.weather.com/current", function(status, body)
        if status == 200 then
            kv:set("weather_data", body, false, 600)  -- cache na 10 minut
        end
    end)
end

Przechowywanie konfiguracji uzytkownika#

-- Zapisz progi z panelu webowego
kv:set("config", {
    temp_min = 18,
    temp_max = 25,
    auto_mode = true
})

-- Odczytaj
local cfg = kv:get("config") or {temp_min = 20, temp_max = 24, auto_mode = false}
if cfg.auto_mode then
    -- ...
end

Plugin API (kvGet / kvSet)#

Pluginy uzywaja metod na obiekcie plugin zamiast globalnego kv:

-- W pluginie:
function Plugin:onInit()
    local token = self:kvGet("api_token", "")
    if token == "" then
        self:log("warn", "Brak tokenu API")
    end
end

-- Zapis
self:kvSet("cache", data, {secure = false, ttl = 300})

-- Sprawdzenie
if self:kvHas("token") then ... end

-- Lista kluczy
local keys = self:kvList("cache:")

-- Usun
self:kvDelete("old_key")

Go bridge#

LuaGoOpis
kv:get(key)__go_kv_get(pluginID, key)Zwraca (value, exists)
kv:set(key, val, secure, ttl)__go_kv_set(pluginID, key, val, secure, ttl)Zapis
kv:delete(key)__go_kv_delete(pluginID, key)Usuwanie
kv:has(key)__go_kv_has(pluginID, key)Sprawdzenie
kv:list(prefix)__go_kv_list(pluginID, prefix)Lista kluczy
kv:getAll()__go_kv_get_all(pluginID)Wszystkie pary