Умный дом: Domoticz.

Тема в разделе "Глядите, что я сделал", создана пользователем ИгорьК, 17 сен 2017.

Метки:
  1. ИгорьК

    ИгорьК Давно здесь

    Любимый мной OpenHab, все таки, не до конца отвечает запросам и чаяниям. Была надежда, что ОН2 станет лучше.

    Поддавшись модным веяниям, вместо оттачивания инструментов, разработчики стали втыкать попугаю цветные перья в задницу. Более того - потеряли и что-то хорошее, что было в первой версии - исчез доступ по паролю и в ближайшее время не ожидается.

    Смотрю Domoticz.
    Домотикз работает с MQTT путем выгона на брокер отчетов о переключении выключателей (вообще всех событий) следующим фейерверком json в теле сообщения(проще говоря - щелчок выключателя на экране и такой комплект на брокер):
    Код (C++):
    {
       "Battery" : 255,
       "RSSI" : 12,
       "description" : "",
       "dtype" : "Light/Switch",
       "id" : "00014055",
       "idx" : 5,
       "name" : "Участок",
       "nvalue" : 0,
       "stype" : "Switch",
       "svalue1" : "99",
       "switchType" : "On/Off",
       "unit" : 1
    }
     
    Как ESP-8266 будет забирать это добро себе? Конечно - в таблицу! При помощи модуля sjson.
    Только прямой обработкой сообщение -> функция sjson не получается.
    Посему dofile("analize.lua") в помощь.
    Код (Lua):
    -- gdata - сообщение от брокера в глобальной переменной
    local msg = gdata
    local nms = ""
    for i = 1, #msg do
        -- выбираем каждый символ сообщения по одному
        local z = string.sub(msg,i,i)
        -- очищаем от всего лишнего
        local zz = string.match(z, "([%w:{}.,\"\'])") or ""
        nms = nms..zz
    end
    if msg then
        -- t - выброс в глобальную таблицу сообщения от domoticz
        t = sjson.decode(nms)
        for k,v in pairs(t) do print(k,v) end
    end
     
    А чтобы слать сообщения на domoticz надо формировать такой стринг
    Код (Lua):
    '{"idx":1,"nvalue":0,"svalue":"25.0"}'
    и гнать его в топик "domoticz/in"
    Об этом впереди.

    UPD 01/10/2017

    Если мы работаем с json (отправляем его на Domoticz и там парсим), то в теле сообщения от Domoticz этот json повторяется в лоб, как и был получен.

    Это явление не ухватывается модулем sjson от nodeMcu и валит устройство на бок.
    Посему код надо подправить так:

    Код (Lua):
    local msg = gdata
    local nms = ""
    local keys = {
        'idx',
        'nvalue',
        'svalue1'
    }
    for i = 1, #msg do
        nms = nms..(string.match(string.sub(msg,i,i), "([%w:{}.,\"\'])") or "")
    end
    print(msg)
    --print(nms)
    local function parce()
        local t = sjson.decode(nms)
        for i=1, #keys do
            print(keys[i], t[keys[i]])
        end
         --[[
            -- Здесь ваш код, если надо что-то сделать
            -- с пришедшим сообщением
            if t.idx == ... then
            ...
            ...
            end
         --]]

    end
    if nms ~= "" then
        pcall(parce)
    end
    Хотя можно поднапрячься и ликвидировать такую проблему, но пока не вижу причины для этого.
     
    Последнее редактирование: 16 окт 2017
    IvanUA, Securbond и BAR__MEN нравится это.
  2. Securbond

    Securbond Гик

    Снова интересная тема ))). Ещё пока просматривал инфу по данному вопросу наткнулся на: node-red https://nodered.org (для объединения всего) и
    ImperiHome http://www.evertygo.com/imperihome (для создания панелей управления).
     
  3. ИгорьК

    ИгорьК Давно здесь

    1. Температура и безопасность.
    Да, ds18b20 в количестве три штуки и вот такая гадость:
    upload_2017-9-19_11-1-39.png
    Эта гадость, хотя и заявлено что работает от трех вольт - не работает!
    Что делает, подлюка: включается, поднимает единицу на выходе, через 15 секунд роняет его в ноль навсегда. Никаких признаков жизни.

    То есть АрдуиноПятьВотльтФорева, к радости отдельностоящих товарищей.
    Ну, в общем, через питалово 5 вольт и делитель из резисторов - работает. (Если что, я не рекламирую, а даже наоборот - посмотрел бы что другое)

    Ну, поехали...
    upload_2017-9-19_18-55-59.png

    upload_2017-9-19_16-17-22.png

    upload_2017-9-19_16-19-57.png

    Общий старт:
    Код (Lua):
    -- Запуск MQTT
    dofile("setmqtt.lua")
    -- Запуск Микроволонового Датчика
    dofile("scurity02.lua")
    -- Запуск измерения температуры
    dofile("ds18b20todomoticz.lua")

    Запуск MQTT
    Код (Lua):
    -- setmqtt.lua
    local myClient = "security"
    local pass = "superpassword"
    local mybroker = "iot.eclipse.org"
    -- mod - глобальная таблица состояния связи
    if not mod then mod = {} end
    mod.broker = false -- есть брокер
    mod.internet = false -- есть Интернет

    local begin = function()
        m = mqtt.Client(myClient, 180, myClient, pass)
        m:lwt(myClient, "OFF", 0, 0)
        connecting = function ()
            -- соединение с брокером - через модуль
            connect = require('_getmqtt')
            connect.connecting(m, mybroker, 1883, myClient, mod, function() connect = nil end)
            -- самоустраняемся
            begin, setmqtt = nil, nil
        end
        -- Потеряли связь? Не беда:
        m:on("offline", function(con)
            mod.broker = false
            mod.interenet = false
            m:close()
            dofile("setmqtt.lua")
        end)
        -- Получили сообщение?
        m:on("message", function(conn, topic, dt)
            local top = string.gsub(topic, myClient.."/","")
            -- print(top..":"..dt)
            -- Выталкиваем сообщение в глобальные переменные
            -- _G.gtopic = top
            _G.gdata = dt
            -- Вызываем анализатор
            dofile("analize.lua")
        end)
        connecting()
    end
    begin()

    Модули.
    Код (Lua):
    -- getmqtt.lua
    local ml = ...
    local M={}
    function M.connecting(m, Broker, port, myCl, mod, unload)
        local getConnect
        local count = 0
        getConnect = function()
           if mod.internet == true then
                m:connect(Broker, port, 0, 0,
                function(con)
                    tmr.stop(getmq)
                    tmr.unregister(getmq)
                    getmq = nil
                    print("Got "..Broker)
                    m:subscribe("domoticz/out",0, function(conn)
                        print("Subscribed.")
                    end)
                    m:publish(myCl,"ON",0,0)
                    _G.mod.broker = true
                    if unload then
                        getConnect, count = nil, nil
                        package.loaded[ml]=nil
                        unload()
                    end
                end)
            else
                print("Wating for WiFi "..count.." times")
                count = count + 1
                if count > 20 then node.restart() end
            end
        end
        getmq = tmr.create()
        getmq:alarm(10000, 1, function()
            getConnect()
        end)
        local call = function()
            print("Got WiFi!")
            wf = nil
        end
        wf = require("_getwifi")
        wf.check(call)
        getConnect()
    end
    return M

    Код (Lua):
    local modn = ...
    local M = {}
    M.check = function(call)
        local isconnect = function()
            tmr.create():alarm(15000, tmr.ALARM_SINGLE,
            function(t)
                t = nil
                M.check(call)
            end)
        end

        local ip = wifi.sta.getip()
        if ip ~= nil then
            net.dns.resolve("www.google.com", function(sk, ip)
                if (ip == nil) then
                    print("DNS fail: Ask Connect!")
                    isconnect()
                else
                    print('Got google: '..ip)
                    if not mod then mod = {} end
                    mod.internet = true
                    if call then call() end
                    package.loaded[modn] = nil
                end
            end)
        else
            isconnect()
        end
    end
    return M

    Код (Lua):
    local M={}
    M.adrtbl = {}
    M.pin = 4
    M.del = 750

    function M.getaddrs(ttable, call)
        ow.setup(M.pin)
        ow.reset_search(M.pin)
        repeat
            local adr = ow.search(M.pin)
            if(adr ~= nil) then
                table.insert(M.adrtbl, adr)
            end
        until (adr == nil)
        ow.reset_search(M.pin)
        M.askTemp(ttable, call)
    end

    function M.askTemp(ttable, call)
        ow.setup(M.pin)
        for _, v in pairs(M.adrtbl) do
            ow.reset(M.pin)
            ow.select(M.pin, v)
            ow.write(M.pin, 0x44, 1)
        end
        v = nil

        tmr.create():alarm(M.del, tmr.ALARM_SINGLE, function (t)
            M.readResult(ttable, call)
            t = nil
        end)
    end

    function M.readResult(ttable, call)
        local data, crc, t
        for _, v in pairs(M.adrtbl) do
            ow.reset(M.pin)
            ow.select(M.pin, v)
            ow.write(M.pin,0xBE,1)
            data = string.char(ow.read(M.pin))
            for i = 1, 8 do
                data = data .. string.char(ow.read(M.pin))
            end
            crc = ow.crc8(string.sub(data,1,8))
            if (crc == data:byte(9)) then
                t = (data:byte(1) + data:byte(2) * 256)
                if (t > 32767) then t = t - 65536 end
                t = t * 625 /10000
                local ts = string.format("%.02f", t)
                table.insert(ttable, ts)
            end
        end
        if call then call(ttable) end
    end

    function M.getTemp(ttable, call, pin, del)
        if #M.adrtbl == 0 then
            M.pin = pin or M.pin
            M.del = del or M.del
            M.getaddrs(ttable, call)
        else
            M.askTemp(ttable, call)
        end
    end

    Измерение и отправка температуры:
    Код (Lua):
    do
    temp = {}
    local pin = 4
    local del = 750

    -- Важная тебличка! Перечисляем idx приемных элементов
    -- в порядке следования датчиков
    idx = {
        19,
        2,
        3
    }

    function myWork()
        print("Got DS18b20: "..#temp)
        table.foreach(temp, print)
        ds = nil
        package.loaded["ds18b20m"]=nil
        local itog = {}
        for i = 1, #temp do
            table.insert(itog, {idx[i], temp[i]})
        end
        local function publNow()
            local p = table.remove(itog)
            if p and _G.mod.broker then
                m:publish("domoticz/in",'{"idx":'..p[1]..',"nvalue":0,"svalue":"'..p[2]..'"}',0,0,publNow)
            end
        end
        if mod and mod.broker then
            publNow()
        end
    end

    local function send()
        temp = {}
        ds = require('ds18b20m')
        ds.getTemp(temp, myWork, pin, del )
    end
    send()
    tmr.create():alarm(30000, 1, send)
    end

    Отправляем данные с датчика движения :
    Код (Lua):
    do
      print("Start Security")
      local pin = 1
      gpio.mode(pin,gpio.INT)
      gpio.trig(pin)
      local function pin1cb(level)
            print("Level now "..level)
            if mod and mod.broker then
                local switchcmd = (level == 1 and "On" or "Off")
                -- "idx":14 - это idx принимающего выключателя в Domoticz
                m:publish("domoticz/in",'{"command":"switchlight","idx":14,"switchcmd":"'..switchcmd..'"}',0,0)
            end
            gpio.trig(pin, level == gpio.HIGH  and "down" or "up")
      end
      gpio.trig(pin, "down", pin1cb)
    end

    Анализируем полученные от Domoticz json(ы):
    Код (Lua):
    local msg = gdata
    local nms = ""
    local keys = {
        'idx',
        'nvalue',
        'svalue1'
    }
    for i = 1, #msg do
        nms = nms..(string.match(string.sub(msg,i,i), "([%w:{}.,\"\'])") or "")
    end
    if nms then
        local function annmg()
            local t = sjson.decode(nms)
            for i=1, #keys do
                print(keys[i], t[keys[i]])
            end
            --[[
            Здесь ваш код, если надо что-то сделать
            с пришедшим сообщением
            if t.idx == ... then...
            ...
            end
            --]]

        end
        pcall(annmg)
    end
     
    Последнее редактирование: 20 сен 2017
    Tomasina и Securbond нравится это.
  4. Как нет?
    Все есть, просто надо это реализовать через nginx.
    Если интересно, могу выложить как это сделать.
     
  5. ИгорьК

    ИгорьК Давно здесь

    Конечно выкладывайте!
    Но, замечу, раньше это было само собой.
     
    Последнее редактирование: 20 сен 2017
  6. Установка и настройка авторизации в openHAB2 через nginx.

    Устанавливаем nginx

    Код (C++):
    sudo apt-get update && sudo apt-get install nginx

    Переносим и переименовываем файл дефолтных настроек

    Код (C++):
    sudo mv /etc/nginx/sites-enabled/default /etc/nginx/sites-enabled/openhab2

    Редактируем файл конфигурации

    Код (C++):
    sudo nano /etc/nginx/sites-enabled/openhab2

    Приводим его к виду:


    Код (C++):
    server {
        listen                          80;
        server_name                     Main;
    }

    server {
        listen                          8081;
        server_name                     openHAB;

        location / {
            proxy_pass                              http://localhost:8080/;
            proxy_set_header Host                   $http_host;
            proxy_set_header X-Real-IP              $remote_addr;
            proxy_set_header X-Forwarded-For        $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto      $scheme;
            satisfy                                 any;
            allow                                   192.168.1.0/24; #Пул адресов с которых разрешен вход без указания логина и пароля
            allow                                   192.168.2.0/24; #Пул адресов с которых разрешен вход без указания логина и пароля
            auth_basic                              "Username and Password Required";
            auth_basic_user_file                    /etc/nginx/.htpasswd;
        }
    }
    Nginx довольно гибкий и поддерживает много тонких настроек, я только указал основные настройки.

    Устанавливаем утилиту от Apache для создания файла паролей
    Код (C++):
    sudo apt-get install apache2-utils
    Создаем файл паролей
    Код (C++):
    sudo htpasswd -c /etc/nginx/.htpasswd username
    !!!ВНИМАНИЕ!!!
    Если вы хотите добавить пользователя в существующий файл паролей, то используйте команду без ключа "-c"

    Код (C++):
    sudo htpasswd /etc/nginx/.htpasswd username
    Для удаления пользователя выполнить команду:
    Код (C++):
    sudo htpasswd -D /etc/nginx/.htpasswd username
    После изменения обязательно выполнять команду перезагрузки nginx
    Код (C++):
    sudo service nginx restart
    Обращаю ваше внимание, что обращение теперь должно быть на порт указанный в настройках файла конфигурации nginx (8081).



    Вот вроде и все, если будут вопросы, пишите, будем разбираться вместе.
     
    petr0vsk и ИгорьК нравится это.
  7. ИгорьК

    ИгорьК Давно здесь

    Класс! Но зря в этой теме. Уже дал ссылку из темы про ОпенХаб.
     
    Последнее редактирование: 21 сен 2017
  8. ИгорьК

    ИгорьК Давно здесь

    А вот чем мне нравится Domoticz. Вошел под паролем наблюдателя, нажал на лампочку включения освещения на участке и получил это:

    upload_2017-9-21_12-25-57.png
     
    Последнее редактирование: 21 сен 2017
  9. Согласен, с правами доступа очень удобно, но для управления по протоколу MQTT, без костылей не получится.
    Domoticz может отслеживать состояние по MQTT, но вот управлять не может.
    Json как вариант, но больно много информации он отправляет в эфир, в некоторых ситуациях это критично.
     
  10. ИгорьК

    ИгорьК Давно здесь

    Не понял - что значит не может управлять по MQTT? Он выдает на брокер необходимую информацию, а исполнительное устройство, подписчик, ее ловит, парсит и что-то делает.
    Пример кода я уже написал в предыдущем посте.

    Я пока не готов что-то комментировать по Domoticz - не вижу общей картины.
    Но то что уже понял - мне очень очень нравится.
    Скрипты - на Lua (в т.ч.), песня!
    Писать скрипты можно прямо из окна браузера, там же сразу логи и ошибки.
    Три типа пользователей: администратор, участник и наблюдатель.
    Отличные графики и система логов событий - тоже из окна.

    Не увидел пока систему вложений окон как в OH, но за Lua вместо Xtend в скриптах готов простить сразу, авансом.

    Вообще, ESP-8266 и скрипты Domoticz на одном языке - то что надо.
     
    petr0vsk нравится это.
  11. ИгорьК

    ИгорьК Давно здесь

    Второй вариант, тоже очень даже - bash скрипты. На них вообще все что хочешь можно сделать.

    upload_2017-9-21_14-5-34.png

    То есть, по умолчанию уже просто силища заложена.
     
  12. Последний раз когда я сталкивался с MQTT на Domoticz, то поддержка там была на уровне
    "Currently only receiving sensor data is supported on Domoticz MQTT. Switching actuators is not supported"
     
  13. ИгорьК

    ИгорьК Давно здесь

    Ну что вы... Все не так. Каждый девайс отправляет на брокер инфо - бери и действуй.
    Хорошо это или плохо - вопрос. Можно и запретить такую отправку, и работать скриптами.
     
  14. Обязательно посмотрю, как будет время. Если все устроит, то заменить OH2 на Domoticz труда не составит, т.к. использую в качестве управляющего звена NodeRed.
    NodeRed.JPG
     
    ИгорьК нравится это.
  15. valeraba

    valeraba Нерд

    С любимыми не расстаются,
    ...но если нет взаимности ;)
     
  16. ИгорьК

    ИгорьК Давно здесь

    Не туда он пошел, не туда!
    Главные претензии к первому:
    - малораспространенный язык скриптов, нет нормальной документации К API
    - "разрозненное" программирование итемы-сайтмэп-правила-персистенсы - все надо ловить по файлам и компоновать.
    - нет разделения прав пользователей.
    Второй
    - добавил путаницу в способы управления
    - и даже парольный вход - по костылю.
    Какая тут взаимность?
    ОН2 начали раскрашивать как задницу попугая, а не упрощать и улучшать взаимодействие с пользрователями.

    Я сейчас сижу на ОН 1.8 и изучаю Domoticz - усиленно копать OH2 желания абсолютно нет.
    Может быть после погружения в эту систему и вернусь к ОН, но пока говорить об этом рано.
     
  17. ИгорьК

    ИгорьК Давно здесь

    Если здесь еще кто-то пытается разобраться с Домотикз, привожу пример, ИМХО, полезного скрипта.
    Скрипт зажигает красную лампочку, если температура на датчике больше 27 и шлет сообщение в телеграм.
    Меньше - гасит лампочку.
    ... и как же все просто! Это вам нe OpenHab.

    Код (Lua):
    commandArray = {}
    if devicechanged then

    token = "ЗДЕСЬ ВАШ ТОКЕН"
    chatid = 11111111 -- чат ID
    message = 'Караул! '


    -- "Зал" - название детчика температуры, его idx = 3
    if devicechanged['Зал_Temperature'] ~= nil then
        if devicechanged['Зал_Temperature']  > 25 then
            -- 'Красная Лампочка' - название выключателя лампочки. Красной лампочки :-)
            commandArray['Красная Лампочка']='On'
            print("\n\nТемпература Повысилась!\n\n")
            ttt = string.format("%.02f", devicechanged['Зал_Temperature'])
            message = message..ttt.." в Зале! Все пропало!"
            os.execute('curl --data chat_id='..chatid..' --data-urlencode "text='..message..'"  "https://api.telegram.org/bot'..token..'/sendMessage" ')
        else
            commandArray['Красная Лампочка']='Off'
            print("\n\nТемпература Понизилась!\n\n")
        end

    for deviceName,deviceValue in pairs(otherdevices) do
        print ("otherdevices '"..deviceName.."', value '"..tostring(deviceValue).."'");
    end

    for deviceName,deviceValue in pairs(otherdevices_svalues) do
        print ("otherdevices_svalues '"..deviceName.."', value '"..tostring(deviceValue).."'");
    end

    for deviceName,deviceValue in pairs(otherdevices_idx) do
        print ("otherdevices_idx '"..deviceName.."', value '"..tostring(deviceValue).."'");
    end

    for deviceName,deviceValue in pairs(otherdevices_lastupdate) do
        print ("otherdevices_lastupdate '"..deviceName.."', value '"..tostring(deviceValue).."'");
    end

    end

    end
    return commandArray
    Также в консоли скрипт показывает состояние всех доступных ему таблиц.
    Естественно, скрипт в этой части только для обучения и тестирования.

    Еще вариант отправки информации на телеграм:
    upload_2017-9-25_17-49-54.png

    В поле "Действие при включении" забивается строка типа:
    Код (C++):
    https://api.telegram.org/bot0000000:KEYKEYKEYKEYKEYKEY/sendMessage?chat_id=12345678&text="ВАШ ТЕКСТ"
     
    Последнее редактирование: 25 сен 2017
    Securbond нравится это.
  18. star003

    star003 Нерд

    Пока не понравилось то, что одна ветка в mqtt сервере. При разборе на плате придется читать все что с нее валится и выбирать свое. Это может быть проблемой.

    В ОН для каждого датчика была своя ветка и это на мой взгляд несколько удобней.
     
  19. ИгорьК

    ИгорьК Давно здесь

    Я уже привел код выше - никаких проблем.
    Не нравится одна ветка - отключите. Отправляйте данные через скрипты bash.
     
  20. ИгорьК

    ИгорьК Давно здесь

    Дело привычки. Надо дольше поковыряться.
    Мне уже кажется Домотикз и проще и логичнее. Хотя не без напряжения мозга в некоторых местах.
    У меня, думаю и у всех, основа работы с устройствами - MQTT. Осталось чуть - до конца разобраться с парсингом json из скриптов и я уже готов переходить на Домотикз.
     
    petr0vsk нравится это.