AccessControl & Expose#

System eksponowania obiektów (expose) i kontroli dostępu (AccessControl) decyduje o tym, które obiekty vCLU są widoczne w Home Assistant (MQTT) i HomeKit, oraz czy można je sterować czy tylko odczytywać.

expose() - eksponowanie obiektu#

expose() rejestruje obiekt Lua w systemie integracji. Bez wywołania expose() obiekt nie pojawi się ani w HA ani w HomeKit.

local handle = expose(obj, typ, opcje)
ParametrTypOpis
objtableObiekt z metodą get(0), opcjonalnie set(0, v), onChange(cb)
typstringTyp eksponowania (patrz tabela niżej)
opcjetableOpcje (opcjonalne)

Opcje#

KluczTypDomyślnieOpis
namestringNazwa wyświetlana w HA/HomeKit
idstringIdentyfikator do generowania ścieżki
pathstringJawna ścieżka (zamiast generowanej)
readonlybooleanfalseTylko odczyt
hiddenbooleanfalseNie eksponuj (zwraca nieaktywny handle)
mqttbooleantrueEksponuj przez MQTT (Home Assistant)
homekitbooleantrueEksponuj w HomeKit
groupstringGrupa (do przyszłego użytku)
areastringSugerowany pokój w Home Assistant

Typy#

TypHAHomeKitOpis
switchswitchSwitchprzełącznik ON/OFF
lightlightLightbulblampa ON/OFF
dimmerlightLightbulblampa z jasnością 0-100
covercoverWindowCoveringroleta z pozycją
sensorbinary_sensorContactSensorczujnik binarny
motionbinary_sensorMotionSensorczujnik ruchu
temperaturesensorTemperatureSensortemperatura
humiditysensorTemperatureSensorwilgotność
numbernumberSwitchwartość liczbowa
sceneswitchSwitch (bezstanowy)scena
buttonbuttonprzycisk (zdarzenie)
locklockzamek
fanfanwentylator
garage_doorcoverbrama garażowa

Przykłady#

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

-- Obiekt z OM registry
expose(_:get("CLU.DOUT1"), "switch", {name = "Lampa kuchnia"})

-- Czujnik temperatury (wartość statyczna lub dynamiczna)
expose({value = 0, get = function(self) return self.value end}, "temperature", {
    name = "Temperatura salon"
})

-- Scena
scene("wieczor", function()
    _:get("CLU.DOUT1"):execute(DOUT.METHOD_SWITCH_ON)
    _:get("CLU.DOUT2"):execute(DOUT.METHOD_SWITCH_OFF)
end)
expose(getScene("wieczor"), "scene", {name = "Tryb wieczorny"})

ExposedHandle - API uchwytu#

expose() zwraca handle, przez który można dynamicznie zmieniać obiekt.

Metody fluent (chainable)#

local h = expose(obj, "switch", {name = "Lampa"})
h:name("Nowa nazwa")    -- zmień nazwę
h:readonly()             -- ustaw jako tylko-odczyt
h:mqttOnly()             -- wyłącz HomeKit
h:homekitOnly()          -- wyłącz MQTT
h:group("salon")         -- ustaw grupę

Chaining:

expose(obj, "switch"):name("Lampa"):readonly():mqttOnly()

Wartość#

h:getValue()        -- odczytaj aktualną wartość
h:setValue(1)        -- ustaw wartość (emituje state_changed)
                     -- nie działa gdy readonly

Aktualizacja w runtime#

-- Zmień nazwę (aktualizuje discovery w HA i HomeKit)
h:rename("Zmieniona nazwa")

-- Zmień opcje (merge)
h:update({readonly = true})
h:update({mqtt = false})          -- ukryj z MQTT, zostaw HomeKit
h:update({homekit = false})       -- ukryj z HomeKit, zostaw MQTT

Usunięcie#

h:unexpose()        -- usuń obiekt z HA i HomeKit
h:isExposed()       -- sprawdź czy nadal wyeksponowany (true/false)

Ścieżka#

h:getPath()         -- zwraca ścieżkę, np. "vclu:switch1" lub "CLU.DOU123"

exposeRegistry() - automatyczne eksponowanie#

exposeRegistry() eksponuje wszystkie obiekty z registry (z OM) jednym wywołaniem:

local count = exposeRegistry()
-- Zwraca: liczba nowo wyeksponowanych obiektów

Reguły#

  1. Obiekty już wyeksponowane ręcznie przez expose() sa pomijane - ręczny expose zawsze wygrywa
  2. AccessControl jest respektowany - ukryte obiekty nie są eksponowane
  3. Typ jest wykrywany automatycznie z TypeMap (DOUT → switch, DIN → sensor, ROLLER → cover, itd.)

Priorytet#

1. Ręczny expose() w user.lua     (najwyższy)
2. Reguły AccessControl           (środkowy)
3. exposeRegistry() automatyczne  (najniższy)

Jeśli w user.lua wyeksponujesz obiekt ręcznie, exposeRegistry() go nie nadpisze:

-- user.lua
expose(_:get("CLU.DOUT1"), "switch", {name = "Moja lampa", readonly = true})

-- Później (np. w bootstrap)
exposeRegistry()  -- pominie CLU.DOUT1, bo już wyeksponowany

AccessControl - kontrola dostępu#

AccessControl pozwala osobno kontrolować widoczność i sterowanie dla każdej integracji (MQTT / HomeKit).

Poziomy dostępu#

PoziomWidocznośćSterowanieOpis
fulltaktakPełny dostęp (domyślnie)
readonlytaknieWidoczny ale nie sterowalny
hiddennienieUkryty - nie pojawia się w integracji

Lua API#

-- Odczytaj dostęp
local level = AccessControl.get("mqtt", "CLU.DOUT1")   -- "full"/"readonly"/"hidden"

-- Helpersy
AccessControl.isHidden("mqtt", "CLU.DOUT1")     -- true/false
AccessControl.isReadonly("mqtt", "CLU.DOUT1")   -- true/false
AccessControl.canControl("mqtt", "CLU.DOUT1")   -- true = level == "full"
AccessControl.canPublish("mqtt", "CLU.DOUT1")   -- true = level ~= "hidden"

-- Pobierz wszystkie reguły
local all = AccessControl.getAll()
-- { ["CLU.DOU1"] = { mqtt = "full", homekit = "hidden" }, ... }
`AccessControl` wpływa na `exposeRegistry()` - decyduje co i jak zostanie wyeksponowane. Ale nie zmienia obiektów które **już są wyeksponowane**. Do dynamicznych zmian na żywym obiekcie używaj `handle:update()`.

Plik konfiguracji#

Reguły zapisywane są w access_control.yaml:

objects:
  CLU221000290.DOU0001:
    mqtt: full
    homekit: hidden
  CLU221000290.DIMM0001:
    mqtt: readonly
    homekit: full
  CLU221000290.DOU0003:
    mqtt: hidden
    homekit: hidden

Obiekty bez wpisu mają domyślny poziom full.

Scenariusze#

Ukryj obiekt z Home Assistant (MQTT), zostaw HomeKit#

-- Przy eksponowaniu
expose(obj, "switch", {name = "Lampa", mqtt = false})

-- Albo w runtime na istniejącym handle
h:update({mqtt = false})

Ukryj z HomeKit, zostaw HA#

-- Przy eksponowaniu
expose(obj, "switch", {name = "Lampa", homekit = false})

-- Albo w runtime
h:update({homekit = false})

Zmień z pełnego dostępu na readonly#

-- Dla obu integracji naraz
h:update({readonly = true})

Całkowicie usuń obiekt z HA i HomeKit#

h:unexpose()

Po unexpose():

  • MQTT: publikuje pusty payload na topic discovery (HA usuwa encję)
  • HomeKit: akcesorium znika po restarcie bridge

Zmień nazwę wyświetlaną#

h:rename("Nowa nazwa")
-- HA: re-publikuje discovery z nową nazwą
-- HomeKit: aktualizuje przy następnym odświeżeniu

Dynamicznie przełączaj dostęp w zależności od pory dnia#

local lock = expose(_:get("CLU.LOCK1"), "lock", {name = "Zamek drzwi"})

-- Scena "nocny" - wyłącz sterowanie zamkiem
scene("tryb_nocny", function()
    lock:update({readonly = true})
end)

-- Scena "dzienny" - przywróć pełne sterowanie
scene("tryb_dzienny", function()
    lock:update({readonly = false})
end)

Ręczny expose przed exposeRegistry#

-- user.lua

-- 1. Ręcznie eksponuj z własną nazwą i ustawieniami
expose(_:get("CLU.DOUT1"), "switch", {
    name = "Lampa salon",
    readonly = true,
    homekit = false
})

-- 2. Reszta obiektów z registry - automatycznie
exposeRegistry()
-- CLU.DOUT1 jest pominięty (już wyeksponowany ręcznie)
-- Reszta obiektów eksponowana z domyślnymi nazwami

Obiekt z onChange - automatyczna aktualizacja stanu#

Jeśli obiekt implementuje onChange(callback), expose automatycznie subskrybuje zmiany:

local MySwitch = {}
function MySwitch:new()
    return setmetatable({_value = 0, _cbs = {}}, {__index = self})
end
function MySwitch:get(f) return self._value end
function MySwitch:set(f, v) self._value = v; self:_notify() end
function MySwitch:onChange(cb)
    table.insert(self._cbs, cb)
    return function()  -- MUSI zwrócić funkcję unsubscribe!
        for i, c in ipairs(self._cbs) do
            if c == cb then table.remove(self._cbs, i); break end
        end
    end
end
function MySwitch:_notify()
    for _, cb in ipairs(self._cbs) do cb(self._value) end
end

local sw = MySwitch:new()
expose(sw, "switch", {name = "Mój przełącznik"})
-- Teraz sw:set(0, 1) automatycznie aktualizuje stan w HA/HomeKit
Metoda `onChange()` **musi zwrócić funkcję unsubscribe**. Bez tego `unexpose()` nie usunie callbacka i dojdzie do wycieku pamięci.

Tymczasowe eksponowanie#

-- Eksponuj obiekt na 5 minut (np. do testów)
local h = expose(obj, "switch", {name = "Tymczasowy"})

after(5 * 60 * 1000, function()
    h:unexpose()
    log.info("Tymczasowy obiekt usunięty")
end)

Jak AccessControl wpływa na exposeRegistry#

Przykład pliku access_control.yaml:

objects:
  CLU.DOU0001:
    mqtt: hidden
  CLU.DOU0002:
    mqtt: readonly
  CLU.DOU0003:
    mqtt: hidden
    homekit: hidden

Wynik exposeRegistry():

ObiektMQTTHomeKitEfekt
CLU.DOU0001hiddenfullexpose z mqtt = false (tylko HomeKit)
CLU.DOU0002readonlyfullexpose z readonly = true
CLU.DOU0003hiddenhiddenpominięty - nie eksponowany wcale
CLU.DOU0004fullfullexpose normalnie (pełny dostęp)