środa, 23 maja 2018

Obsługa zdarzeń w Domoticz używając dzVents

Osobiście nie lubię terminu 'inteligentny dom'. Inteligentny może być człowiek, dom może być umiejętnie sterowany na podstawie pewnych zdarzeń. Chociaż oglądając ostatnie poczynania Google Duplex zaczynam powoli zastanawiać się nad zmianą mojej opinii ;)

W poprzednim wpisie opisałem proste zdarzenia na czujnikach, switch'ach, itp. W Domoticz jest (jak napisałem) możliwość wykorzystania kilku schematów zdarzeń:

- bezpośrednio na samych czujnikach
- schematy blokowe w Blockly
- skrypty Lua lub Python
- dzVents

Dzisiaj skupię się na dzVents - wewnętrznym języku skryptów Domoticz. W sumie bazuje na Lua, czyli załatwiamy po części punkt trzeci ;) Takie małe skróty.

Warto zainwestować czas w naukę, ponieważ schematy Blockly, mimo że ładne, swoje ograniczenia mają. Nadmieniam - nie jestem w tym temacie (jeszcze) ekspertem - to moje początkowe próby.

Opieram się na wersji 2.2.0, dostępnej w ostatniej stabilnej wersji Domoticz!

Dokumentacja tej wersji dostępna jest pod linkiem:
https://github.com/domoticz/domoticz/blob/9f75e45f994f87c8d8ce9cb39eaab85886df0be4/scripts/dzVents/documentation/README.md

No bo proszę, jakże naturalne jest wpisanie:

Co 10 minut w dni powszednie uruchom akcje:
- Jeżeli to nie wieczór i nie noc, przepisz wartość zmiennej do innej zmiennej, upewnij się, że światła w salonie wyłączone, a do tego, jeżeli czujka ruchu nie odpowiada - wyłącz światło w łazience

return {
active = true,
on = {
timer = {'Every 10 minutes on mon,tue,wed,thu,fri'}
},
execute = function(domoticz)
if (domoticz.time.isDayTime and domoticz.variables('Zmienna1').value == 10) then
domoticz.variables('Zmienna2').set(15)

domoticz.setScene('Światła Salon', 'Off')

if (domoticz.devices('CzujkaRuchu').lastUpdate.minutesAgo > 5) then
                domoticz.devices('Bathroom lights').switchOff()
            end
end
end
}

To tylko jednak przykład. Sam używam kilku innych skryptów, które okazały się bardzo przydatne.

Zacznijmy od tego jak je pisać i gdzie możemy je znaleźć.

Setup -> More Options -> Events. Tam znajdziemy edytor z kilkoma możliwymi typami:
- Blockly
- Lua
- dzVents
- Python

Nas interesuje aktualnie dzVents i taki typ musimy wybrać przy tworzeniu nowego pliku. Nie zapomnijcie również wpisać nazwy oraz zmienić Event active na włączony. Dla wyjaśnienia - skrypty można tworzyć i z poziomu Raspberry Pi, z basha zapisując je w odpowiednim folderze, ale tutaj mamy pewność, że Domoticz sam je sobie w dobrym miejscu osadzi. I będzie mniej problemu przy przenoszeniu systemu.

Mała sugestia - sprawdzajcie log Domoticz, szczególnie na początku tworzenia - często są tam przydatne informacje - czy skrypt się załadował, czy wystartował, jak działa, w jakiej zmiennej się pomyliliśmy, jaka funkcja ma złą nazwę (patrzcie dokładnie ma małe/duże litery - ma to ogromne znaczenie!).






1. Na początek - nie podobało mi się jak barometr Xiaomi jest prezentowany w Domoticz. Pokazuje złe wartości. Oczywistym jest, że ciśnienie 969 Bar nie jest poprawne... Głupota, szczegół? Owszem, ale uważam, że ten szczegół trzeba poprawić.


No to raz, dwa, trzy:

return {
    active = true,
    on = {
        ['timer'] = {'every 30 minutes'}
    },
    execute = function(domoticz)

    local Baro = domoticz.devices('Xiaomi Baro Kuchnia').pressure
    
    domoticz.devices('Baro Kuchnia').updateBarometer(Baro, domoticz.BARO_NOINFO)
        
    end
}

Zwróćcie uwagę na konstrukcję:
- początek funkcji
- warunek wyzwolenia - tutaj czasowy
- zawartość funkcji

Co zrobiliśmy? Co 30 minut uruchom przepisanie wartości z czujnika do zmiennej, a później zmienną przepisz do osobno założonego czujnika ciśnienia.



I już, działa.

Zauważcie, że czujnik ciśnienia można zaktualizować o parametry, tutaj BARO_NOINFO (czyli nie zmieniamy), ale może on przyjmować również wartości:
BARO_CLOUDYBARO_CLOUDY_RAINBARO_STABLEBARO_SUNNYBARO_THUNDERSTORMBARO_NOINFOBARO_UNSTABLE

Nie jestem fizycznie w stanie opisać tutaj wszystkich parametrów wszystkich czujników - zapraszam do czytania dokumentacji i własnych prób.

2. Kolejny temat - biegam. Dużo biegam. Wstaję rano, banan, woda i w drogę. Ale lubię wiedzieć jak mam się ubrać. Albo inny przykład - w zimie warto wiedzieć czy jest przymrozek.

Oczywiście - można sprawdzić prognozę pogody albo wyjrzeć za okno, ale często gdy wstanę sprawdzam pocztę i akurat takie podstawowe informacje mi pomagają.

return {
    on = {
        ['timer'] = {'at sunrise'}
    },
    execute = function(domoticz)
    local Tmp = domoticz.devices('Temp na zewnątrz').temperature
   
    function round(num, numDecimalPlaces)
          local mult = 10^(numDecimalPlaces or 0)
          return math.floor(num * mult + 0.5) / mult
        end
   
        domoticz.notify('Temperatura', 'O wschodzie słońca było ' ..round(Tmp,1), domoticz.PRIORITY_NORMAL)
    end
}

Tytułem wytłumaczenia - funkcja round() pojawiła się w nowszej wersji dzVents, ja musiałem poszukać innego rozwiązania (Łukasz Rybak - dziękuję).


W sumie przyszło mi do głowy w trakcie pisania, że dobrze byłoby rozwiązanie uczynić bogatszym o dane większej ilości czujników z domu - temperatura w kilku pokojach, status świateł, itp. Zapisuję na liście 'Do zrobienia' :)

3. Jest sobie u mnie w domu ekran do projektora. Wisi (Tak przy okazji to zestaw ekran + projektor to jeden z lepszych zakupów w życiu). Stworzyłem sceny, np. 'Oglądamy film', które wyłączają światła w salonie, włączają subwoofer, projektor i obniżają ekran. Problem z nim jednak był taki, że nie rozwijam go do końca, tylko musi się zatrzymać w konkretnej pozycji. Jak, po wydaniu komendy 'W dół', zatrzymać go? dzVents ma funkcję stop().afterSec().

return {
    active = true,
    on = {
        devices = {
            'Ekran'
        }
    },
    execute = function(domoticz,switch)
        
        if (switch.state == 'On') then
            switch.stop().afterSec(51)
        end
    end
}

Czyli start ręcznie w Domoticz, czekamy 51 sekund, stop. Bajka.

4. Kolejny temat - powiadomienia przy pewnych warunkach. Tutaj - zbyt wysoka temperatura sugeruje, że coś złego może się dziać w domu. Oczywiście można na każdym z osobna to 'wyklikać', ale nie jest to zbyt optymalne.

return {
    active = true,
    on = {
        devices = {
            'Temperatura Salon',            
            'Temperatura Kuchnia',            
            'Temperatura Sypialnia',        
            }
    },
    execute = function(domoticz,device)
        
        if (device.name == 'Temperatura Salon' and device.temperature >= 45) then
            domoticz.email('Możliwy pożar!', 'Zbyt wysoka temperatura!', 'adres@gmail.com')
        end 
        if (device.name == 'Temperatura Kuchnia' and device.temperature >= 45) then
            domoticz.email('Możliwy pożar!', 'Zbyt wysoka temperatura!', 'adres@gmail.com')
        end
        if (device.name == 'Temperatura Sypialnia' and device.temperature >= 45) then
            domoticz.email('Możliwy pożar!', 'Zbyt wysoka temperatura!', 'adres@gmail.com')
        end        
    end
}

Dodajcie swoje warunki (np. tylko w nocy, bo wtedy na pewno nie świeci słońce na termometr) i do dzieła!

5. Jak do tej pory najciekawsza funkcja, którą zrobiłem to 'Tryb wakacyjny'. 

Nie ma się co śmiać, dopiero zaczynam ;)

Mam oto przełącznik w Domoticz, który zowie się 'Wakacje'. I pod niego powoli podpinam różne akcje. Na przykład: gdy wyjedziemy, Domoticz ma włączyć światła o losowej minucie po 22:00 (ale w zakresie 30 minut) i wyłączyć również losowo po 23:30. Taki symulator tego, że ktoś jest w domu.

local RANDOM_DELAY_MINS = 30
return {
active = true,
on = {
['timer'] = {
'at 22:00',
'at 23:30'
}
},
execute = function(domoticz,_,triggerInfo)

        if (domoticz.devices('Wakacje').state == 'On') then

if (domoticz.devices('Kinkiety').state == 'Off') and (triggerInfo.trigger == 'at 22:00')
then 
domoticz.devices('Kinkiety').switchOn().withinMin(RANDOM_DELAY_MINS)
domoticz.email('Światła włączone!', 'Światła włączone!', 'adres@gmail.com')
else 
    domoticz.devices('Kinkiety').switchOff().withinMin(RANDOM_DELAY_MINS)
domoticz.email('Światła wyłączone!', 'Światła wyłączone!', 'adres@gmail.com')
end

end

end
}

Mam tam jeszcze kilka innych 'ifów', ale ważny jest początek i sens funkcjonalności. Rozbudować go można dowolnie - włącz raz na górze, raz na dole, w zależności od dnia tygodnia włącz różne, itp., itd. Ogranicza Was wyobraźnia. No i czas :D

6. Funkcja z dnia wczorajszego ;) Powiadomienie, jeżeli drzwi są otwarte zbyt długo, a jeżeli są, to przez jaki czas. Dla drzwi na taras dałem limit 30 minut, bo są częściej i dłużej otwierane. Dla drzwi na inny taras - krótszy okres, bo praktycznie ich nie używamy. Zwróćcie uwagę na drugi warunek - jeżeli czas jest mniejszy niż 3 * limit - czyli w tym przypadku na przykład dla 'Drzwi - czujnik' jest to 90 minut - przestań wysyłać powiadomienia, bo widocznie tak ma być. Oczywiście po zamknięciu zmienne zostaną wyzerowane.

local devicesToCheck = {
{ ['name'] = 'Drzwi - czujnik', ['threshold'] = 30 },
{ ['name'] = 'Drzwi mały taras', ['threshold'] = 10 },
}

return {
active = true,
    on = {
        timer = {'every 10 minutes'},
    },
    logging = {
        level = domoticz.LOG_DEBUG,
        marker = "Door"
    },    

execute = function(domoticz)
    local times = 3
for i, deviceToCheck in pairs(devicesToCheck) do
local name = deviceToCheck['name']
local threshold = deviceToCheck['threshold']
local state = domoticz.devices(name).state
local minutes = domoticz.devices(name).lastUpdate.minutesAgo

if ( state == 'Open') then 
    if (minutes > threshold) and (minutes < 3 * threshold) then
        domoticz.notify('Device ' .. name .. ' otwarte ' .. minutes .. ' minut.', domoticz.PRIORITY_HIGH)
                end
end
end
end
}


Jak na razie to koniec - mam jeszcze kilka zdarzeń podpiętych, ale nie są aż tak warte opisania. Do tego kilkanaście kolejnych w głowie, które muszę sobie stworzyć.

Dzięki temu system nabiera sensowności. Bo cóż z tego, że sprawdzam temperaturę, jeżeli nic za tym nie idzie? Dopiero obudowanie Domoticz reagowaniem na czynniki zewnętrzne tworzy nam rozwiązanie godne miana 'Smart home'.

Przyjemnego kodowania życzę!

29 komentarzy:

  1. Cześć

    Buduje właśnie system na Domoticzu i powoli przenoszę urządzenia z Fibaro. Bardzo dużo nauczyłem się z Twojej strony. Mam pytanie odnośnie komunikacji urządzenia Wi-Fi z Domoticz.
    Mam radio internetowe z możliwością obsługi przez aplikację na androida. Mogę włączyć i wyłączyć radio. Ustawić stacje itp. Czy wiesz może jak podpatrzyć komendy wysyłane przez telefon? Docelowo chciałbym uruchamiać radio czujnikiem obecności poprzez Domoticz.

    OdpowiedzUsuń
    Odpowiedzi
    1. Cześć,

      Dziękuję bardzo za miłe słowa, nawet nie zdajesz sobie sprawy jak dużo one dają :)

      Co do radia - sam tego nie robiłem, ale zacząłbym od Wireshark i tcpdump. Wtedy, odpowiednio analizując logi powinno się udać dowiedzieć co jest wysyłane i w jaki sposób.

      Usuń
  2. Jak rozwiązałeś kwestię round() we wcześniejszej wersji DzVents? Zacząłem sobie robić skrypt, który rano wyśle mi minimalną temperaturę z nocy i napotkałem ten problem.

    OdpowiedzUsuń
    Odpowiedzi
    1. W skrypcie, który wrzuciłem tutaj, jest funkcja round():

      function round(num, numDecimalPlaces)
      local mult = 10^(numDecimalPlaces or 0)
      return math.floor(num * mult + 0.5) / mult
      end

      Usuń
  3. Hej tak się upewnię ten przełącznik wakacje włącza u Ciebie losowy przełącznik który ma w nazwie ('Kinkiety') ?

    OdpowiedzUsuń
    Odpowiedzi
    1. Cześć.

      Nie. Mam dwa kinkiety podłączone do jednego włącznika. Oba są sterowane razem przez klawisz włącznika Xiaomi. Ten klawisz jest opisany jako 'Kinkiety' i on ma się załączyć w 'trybie wakcyjnym'

      Usuń
  4. Punkt numer cztery. Powiadomienia. interesuje mnie coś takiego tylko zamiast temperatury ma być ON/OFF oraz zamiast wysyłania maila ma wysłać smsa. Mam coś takiego zrobione w lua, jednak na kilku skryptach a jak to zrobić aby było tak jak u Ciebie?
    Jeden ze skryptów działających u mnie:

    commandArray = {}
    if
    (devicechanged['Uwaga Pożar'] == 'On') then
    os.execute('gammu sendsms TEXT 000000000 -text "UWAGA!!! Pożar w kotłowni"')
    end
    return commandArray

    OdpowiedzUsuń
    Odpowiedzi
    1. Ma ktoś pomysł dlaczego z Domoticza nie idą mi SMSy? Z konsoli idzie ok a z Domoticza nie.

      Usuń
  5. Witam,

    Od dłuższego czasu regularnie śledzę Twojego bloga i czerpię z niego wiedzę oraz inspirację do pracy nad własnym systemem domoticz. Aktualnie pracuję nad sterowaniem roletami wewnętrznymi. Jestem na etapie projektowania oraz drukowania mechaniki do zabudowania w oryginalnych kasetach. Dopiero zaczynam przygodę z programowaniem i nie jestem w stanie sam pisać skryptów do domoticza. Chciałbym prosić Ciebie o tutorial krok po kroku jak skonfigurować domoticza do pracy z owntracks przez mqtt.

    OdpowiedzUsuń
    Odpowiedzi
    1. Cześć!

      Po pierwsze - niesamowicie miło czytać takie komentarze. Ze względu na okres letni trochę zwolniłem z nowymi pomysłami oraz blogiem. Najwyższa jednak pora wrócić do pracy.

      Niestety, w przypadku mqtt nie jestem (jeszcze mam nadzieję) w stanie pomóc. Ale jedno wiem - MQTT w Domoticz to tylko, można powiedzieć, szkielet. Jeżeli naprawdę chcesz się oprzeć na MQTT - wybierz Home Assistant.

      Usuń
    2. Mam już urlop za sobą więc również mogę wrócić do tego co lubię i nie tylko niestety...Domoticza mam zainstalowanego na Synology Ds214 a na nim nie mogę zainstalować Home Assistant ponieważ nie ma na mojego DS214 wymaganej wersji Pythona żeby można było odpalić Home Assistant. Może jęsli nie MQTT bezpośrednio to może napiszesz coś na temat współpracy Domoticza z Node Red? Pozdrawiam serdecznie.

      Usuń
    3. Nie mam w tym zakresie żadnej praktyki, ale z chęcią się douczę :D

      Usuń
  6. Witam!
    Proszę o pomoc.mam podłączony czujnik zużycia energii iNode do domoticz, jak zrobić script który będzie pokazywał dane z czujnika na esp+oled. 😉

    OdpowiedzUsuń
    Odpowiedzi
    1. Cześć. Sam jeszcze tego nie zdążyłem zrobić (OLED mi się jakoś zepsuł i czekam na przesyłkę z Chin z kolejnymi), ale polecam sprawdzić tutaj: https://blog.jokielowie.com/2016/04/domoticz-cz-3-1-esp-easy-na-esp8266-dodajemy-wyswietlacz-oled-dht22-oraz-bh1750/. Łukasz bardzo dokładnie to opisał.

      Usuń
  7. Tylko mam problem z tym aby pokazywał tylko zużycie całkowite. Pokazuje na oled w formacie 999.0;1234567.0 (waty;Kwh),a chwiałbym aby tylko pokazywał to 12345 (kWh). Jak to zrobić. Scrypt lua:
    local Emeter="Emeter"
    commandArray = {}

    if devicechanged[Emeter] then
    commandArray['OpenURL']='192.168.8.20/control?cmd=oled,3,1,'..otherdevices_svalues[Emeter]..''
    end
    return commandArray

    OdpowiedzUsuń
  8. Cezarze,
    Próbuję zmodyfikować trochę Twój skrypt odnośnie porannej temperatury, ale na razie bez sukcesu. Chciałbym rozszerzyć ten skrypt o ciśnienie i wilgotność (tak, aby poranna prognoza była kompletna). Korzystam z wirtualnych przycisków i Airly.
    I próbuję zmodyfikować skrypt poprzez:
    dodanie czujników w linii deklaracji
    local Tmp = domoticz.devices('Temp na zewnątrz').temperature poprzez dodanie "and" i kolejnych czujników. Nie bardzo wiem jakie deklaracje przyjąć dla:
    wilgotności - po zamknięciu nawiasu - humidity?
    ciśnienia - pressure?
    Przy testowaniu pojawia się błąd, mnożenia wartości przez zero. Coś więc nie gra.
    Próbowałem również dodatkowe czujniki deklarować oddzielnie, jako local.temp1..; local.temp2... itd
    Niestety nie bardzo wiem jak to "zmontować" do zapisania w przesyłanej wiadomości.

    OdpowiedzUsuń
  9. Kombinuję trochę w skrypcie, ale niestety wychodzi trochę moja ignorancja w składni. Dopisałem kolejne czujniki w deklaracji:
    local Temp = domoticz.devices('Pogoda -Temperatura').temperature
    local Pres = domoticz.devices('Pogoda - Ciśnienie powietrza').number
    local Hum = domoticz.devices('Pogoda - Wilgotność').number
    local Weather = ('Temp') and ('Pres') and ('Hum').number

    W ostatniej linii zmieniłem nazwę zmiennej na Weather i miałem nadzieję na dane. Niestety w logach wyrzuca mi błąd:
    ...Sunrise.lua:14: attempt to perform arithmetic on local 'num' (a nil value).
    Z tego co rozumiem, wiersz 14 skryptu odnosi się do funkcji "round" - tam nie dokonywałem zmian. Domyślam się, że jest jakiś błąd w działaniu i jakieś dane nie są numeryczne... Ale na razie nie mogę wpaść na przyczynę. A może od początku idę niewłaściwym tokiem myślenia...

    OdpowiedzUsuń
    Odpowiedzi
    1. Cześć.

      Dodałem u siebie na szybko linię
      local TmpZ = domoticz.devices('Temperatura Salon').temperature
      local HumS = domoticz.devices('Temperatura Salon').humidity

      oraz:
      ..', wilgotność: '..round(HumS,1) i dostałem poprawne wartości w e-mailu.

      Co do ciśnienia, tutaj jeszcze w sumie nie wiem, ale w sumie pressure powinno zadziałać.

      Usuń
  10. Hej,
    Racja - wilgotność dla .humidity działa. Niestety pressure nie działa.
    Przy takim zapisie, każdy parametr jest wysyłany oddzielnie - próbowałem wykombinować, aby 3 parametry zostały wysłane w jednym mailu. Na razie bez sukcesu - muszę pokombinować.

    OdpowiedzUsuń
    Odpowiedzi
    1. Hm, jak łączysz teksty kropkami to nie działa?

      Usuń
    2. hej koledzy, jak powinna wyglądać końcowa składnia, aby w jednym powiadomieniu były zawarte np. dwie - trzy temp. itp. z kilku czujników? kombinowałem już na kilka sposobów i albo wyświetla mi jedna lub wcale ... poległem :/

      Usuń
  11. Hej, pytanie odnośnie sensora cisnienia xiaomi. Pokazuje mi ciśnienie 966hPa gdzie aktualnie w moim miejscu zamieszkania wg, prognozy jest 1012 hPa. Domyślam się, że chodzi o ciśnienie względne/bezwzględne. Macie pomysł jak poprawić odczyty Xiaomi?

    OdpowiedzUsuń
    Odpowiedzi
    1. Wydaje mi się, że najlepiej będzie stworzyć wirtualny czujnik i tam korygować.

      Usuń
  12. Panowie pomóżcie w nietypowym problemie.
    Mam dwie instancje Domoticza w dwóch różnych lokalizacjach. Lokalizacje są spięte po VPN, ale to mało istotne.
    Istotne zaś jest to, że chciałbym mieć wszystkie wartości i czujniki z jednego na tym drugim. Niby jest opcja "Domoticz remote server" ale nie działa ona z virtualnymi czujnikami, a prawie wszystkie mam takie.
    Czy istnieje inny w miarę prosty sposób na taką synchronizację

    OdpowiedzUsuń
    Odpowiedzi
    1. Oj, tu może być problem jeżeli chodzi o automatyczne działanie Domoticz. Ale z innej beczki - może warto do tego użyć na przykład InfluxDB/Grafana do raportowania z obu?

      Usuń
  13. Panowie, pomocy, jak przy pkt 4 i 5 zrobić powiadomienia na telegram??

    OdpowiedzUsuń
  14. Witam,

    Walczę z jedną rzeczą.
    Gdy użyję domoticz.notify z wysyłką na telegram, dostaję również mejla.
    Istnieje możliwość wysyłania wyłącznie na telegram ?

    OdpowiedzUsuń
    Odpowiedzi
    1. Niestety, nie jestem w stanie pomóc :( Może ktoś z obecnych tutaj?

      Usuń
  15. Ten wpis jest bardzo interesujący

    OdpowiedzUsuń