Как сделать обычный дверной звонок умным
Сегодня я расскажу о том, как автоматизировать обычный дверной звонок, чтобы он умел работать по расписанию, в беззвучном режиме, а также слать уведомления на смартфон при нажатии на кнопку. В этом мне поможет система домашней автоматизации на базе Home Assistant.
Однажды возникла необходимость выключать звук дверного звонка, чтобы случайные посетители перестали меня беспокоить, особенно в ночное время.
Я начал с того, что определил схему подвода питания к звонку. Фаза уже присутствовала на нём, вместо того, чтобы подаваться на него в момент нажатия кнопки. В дальнейшем это сыграет свою роль.
Для реализации задуманного функционала я добавил в цепь реле. Это двухканальный релейный модуль от компании Fibaro. Работает по протоколу Z-Wave. Почему именно он, станет понятно чуть позже.
Хотя, конкретно в этом месте можно использовать любое другое устройство, будь то Fibaro, Sonoff или простой релейный модуль для Arduino.
Схема подключения
Фаза подаётся на вход реле, а из первого выхода она идёт на звонок. Нулевой провод остаётся нетронутым.
В случае, если звонок подключен так, что кнопка разрывает фазу, а не ноль,
то схема подключения будет такой — кнопка не будет работать, пока выключен выход реле O1:
Либо такой, где фаза при нажатии кнопки подается на вход реле, а звонок звенит, только, если выход О1 включен:
Программная часть
Я использую Home Assistant. Это довольно молодая, активно развивающаяся, очень мощная и гибкая система домашней автоматизации. Поддерживается огромным мировым сообществом энтузиастов, которые занимаются разработкой как самой платформы, так и компонентов, расширяющих ее функционал для работы со множеством устройств, платформ и сервисов.
Home Assistant у меня работает на Raspberry Pi 3 в Docker-контейнере. Поставляется в виде готового решения под названием Hass.io.
Также к Raspberry Pi у меня подключен Z-Wave-контроллер в виде платы расширения.
После подключения модуля к сети и добавления его в Home Assistant, для удобства, я переименую Z-Wave-ноду и поменяю имя и ID объекта непосредственно у реле.
После добавления устройства в конфигурационный файл, оно появляется в интерфейсе. Этот переключатель позволяет управлять звонком в ручном режиме.
Для работы по расписанию я покажу пример, допустим, чтобы звонок не реагировал на нажатие кнопки с 10 вечера до 9 утра.
Node-RED — это open-source инструмент, который служит для связи железа и сервисов. Здесь используются графические линий связи, по которым пересылаются сообщения между узлами. Таким образом различные блоки связываются просто мышкой без использования программирования. Вся разработка в Node-RED ведется через браузер, а ядро можно запустить на различных платформах.
У меня оно также работает в Docker-контейнере в виде плагина для Hass.io.
Я добавил interval node, которая раз в минуту будет дёргать следующую ноду с настройкой периода времени, где я указал желаемый диапазон.
Если текущий момент времени попадает в заданный мною интервал, то звонок выключится после проверки его состояния.
Если он уже был выключен ранее, то команда не пойдет дальше. Аналогично и со вторым выходом.
Если текущее время не попадает в интервал, то проверяем статус, и включаем звонок, если этого не было сделано ранее.
Код этого Node-RED-flow представлен ниже:
[{"id":"e0f64da0.c0817","type":"interval","z":"fd15b534.04ee48","name":"interval","interval":"1","onstart":true,"msg":"ping","showstatus":false,"unit":"minutes","statusformat":"YYYY-MM-D HH:mm:ss","x":110,"y":160,"wires":[["b43c9154.0a918"]]},{"id":"b43c9154.0a918","type":"time-range-switch","z":"fd15b534.04ee48","name":"Sleeptime","lat":"55.00","lon":"37.00","startTime":"22:00","endTime":"09:00","startOffset":"0","endOffset":0,"x":260,"y":160,"wires":[["70939410.71bf1c"],["a71468e9.d47168"]]},{"id":"70939410.71bf1c","type":"api-current-state","z":"fd15b534.04ee48","name":"Статус звонка","server":"b9490f48.586ed","version":2,"outputs":2,"halt_if":"on","halt_if_type":"str","halt_if_compare":"is","entity_id":"switch.corridor_doorbell","state_type":"str","blockInputOverrides":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"entity"}],"override_topic":false,"state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","x":460,"y":140,"wires":[["f28e5d04.a653b"],[]]},{"id":"f28e5d04.a653b","type":"api-call-service","z":"fd15b534.04ee48","name":"Дверной звонок","server":"b9490f48.586ed","version":3,"debugenabled":false,"service_domain":"switch","service":"turn_off","entityId":"switch.corridor_doorbell","data":"","dataType":"jsonata","mergecontext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":680,"y":140,"wires":[[]]},{"id":"a71468e9.d47168","type":"api-current-state","z":"fd15b534.04ee48","name":"Статус звонка","server":"b9490f48.586ed","version":2,"outputs":2,"halt_if":"off","halt_if_type":"str","halt_if_compare":"is","entity_id":"switch.corridor_doorbell","state_type":"str","blockInputOverrides":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"entity"}],"override_topic":false,"state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","x":460,"y":180,"wires":[["9796db3.788e328"],[]]},{"id":"9796db3.788e328","type":"api-call-service","z":"fd15b534.04ee48","name":"Дверной звонок","server":"b9490f48.586ed","version":3,"debugenabled":false,"service_domain":"switch","service":"turn_on","entityId":"switch.corridor_doorbell","data":"","dataType":"jsonata","mergecontext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":680,"y":180,"wires":[[]]}]}]
Уведомление в Telegram
Как раз для этого идеально подходит релейный модуль от Fibaro, т.к. он имеет возможность управления выходами с помощью подключенных выключателей
или кнопок к клеммам S1 и S2.
При подаче фазы на один из этих входов срабатывает соответствующий ему выход реле O1 или O2. Так как у меня выход O1 уже занят самим звонком, то я задействую вход S2.
Из-за того, что в моём случае схема такова, что кнопкой разрывается ноль, а на вход S2 нужно подать фазу, то задача усложняется. Потребуется дополнительный элемент в виде твердотельного реле, управляемого переменным током.
К одному из входов для обеспечения работы этого реле подключается фаза. Эта же фаза подключается и к одному из выходов, для дальнейшей её передачи на модуль в качестве управляющего сигнала. Ноль для питания реле берём от звонка.
Таким образом, при нажатии на кнопку, реле включается, его выход закрывается,
и управляющий сигнал попадает на вход S2 модуля Fibaro.
Опять же, в случае, где кнопкой разрывается фаза, всё гораздо проще, и надо всего лишь соединить между собой общий вход реле и вход S2.
Итак, с железной частью разобрались. Перейдем к настройке.
В свойствах релейного модуля (конфигурация Z-Wave) необходимо обязательно задать два параметра. Первый отвечает за то, что при нажатой кнопке реле включено, а при отпущенной выключено.
А второй то, что кнопка является двухпозиционным выключателем. Иначе схема не будет работать.
Аналогично меняю имя и ID у переключателя, который соответствует второму реле модуля Fibaro,
и перехожу к созданию сценария для получения уведомления.
- Далее включаем ограничение получения сообщений не чаще одного раза в 2 минуты, чтобы не плодить уведомления при многократном нажатии кнопки.
Для взаимодействия с telegram-ботом необходимо установить модуль
node-red-contrib-telegrambot для Node-RED.
- Выбираем своего настроенного бота, в параметрах которого заполняется имя, токен, полученный при создании бота, и разрешенные для общения с ним Chat ID через запятую.
- Указываем свой Chat ID для получения сообщений от бота и произвольный текст уведомления в виде шаблона, взятого из документации к ноде, работа методов которой, в свою очередь, основана на документации Telegram Bot API.
Снимок с камеры при звонке
Как видно, сообщение почти сразу приходит после нажатия кнопки.
Как и задумано, повторное нажатие кнопки ничего не даёт.
Дальше мне захотелось видеть звонящего. Я остановил свой выбор на аналоговой камере в виде дверного глазка.
Единственным её плюсом является возможность установки в дверь на уровне глаз. Потому как низкая стоимость камеры нивелируется необходимостью покупки видеорегистратора для получения цифрового сигнала.
Для получения снимка с камеры в уведомлении изменим сценарий, добавив ноду
HTTP request.
В ней указываем адрес, который отдаёт статическое изображение (его можно взять из документации к камере или видеорегистратору).
Я заранее подключил камеру к Home Assistant, поэтому ссылка ведет к нему.
В качестве результата запроса выбираем ”a binary buffer”.
Следом добавляем ноду function, где формируем содержимое согласно документации telegram-ноды по отправке изображений. Содержимым снимка будет полученный ранее ответ от ноды HTTP request.
Далее добавляем telegram-ноду payload. В ней указываем Chat ID и метод отправки. Предполагается, что бог уже заранее настроен.
Последней я решил добавить telegram-ноду для отображения кнопки получения дополнительного снимка на случай неудачного первого кадра.
Код этого Node-RED flow представлен ниже:
[{"id":"9f016b89.edeb18","type":"server-state-changed","z":"fd15b534.04ee48","name":"Звонок в дверь","server":"b9490f48.586ed","version":3,"exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"entityidfilter":"switch.corridor_doorbell_ding","entityidfiltertype":"substring","outputinitially":false,"state_type":"str","haltifstate":"on","halt_if_type":"str","halt_if_compare":"is","outputs":2,"output_only_on_state_change":false,"for":"","forType":"num","forUnits":"minutes","ignorePrevStateNull":false,"ignorePrevStateUnknown":false,"ignorePrevStateUnavailable":false,"ignoreCurrentStateUnknown":false,"ignoreCurrentStateUnavailable":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"eventData"},{"property":"topic","propertyType":"msg","value":"","valueType":"triggerId"}],"x":140,"y":340,"wires":[["96fbc9f0.88a2e8"],[]]},{"id":"96fbc9f0.88a2e8","type":"delay","z":"fd15b534.04ee48","name":"","pauseType":"rate","timeout":"5","timeoutUnits":"seconds","rate":"1","nbRateUnits":"120","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":true,"x":360,"y":340,"wires":[["65c28ed9.0a8e6"]]},{"id":"65c28ed9.0a8e6","type":"stoptimer","z":"fd15b534.04ee48","duration":"1","units":"Second","payloadtype":"str","payloadval":"","name":"Timeout","x":600,"y":340,"wires":[["4a1d76bc.4213b8"],[]]},{"id":"6a886333.f832cc","type":"telegram event","z":"fd15b534.04ee48","name":"Еще снимок","bot":"f6e2842.9c49078","event":"callback_query","autoanswer":true,"x":510,"y":300,"wires":[["7d5306e4.b68658"]]},{"id":"7d5306e4.b68658","type":"switch","z":"fd15b534.04ee48","name":"","property":"payload.content","propertyType":"msg","rules":[{"t":"eq","v":"RING MORE PIC","vt":"str"}],"checkall":"true","repair":false,"outputs":1,"x":635,"y":300,"wires":[["4a1d76bc.4213b8"]],"l":false},{"id":"4a1d76bc.4213b8","type":"http request","z":"fd15b534.04ee48","name":"","method":"GET","ret":"bin","paytoqs":"ignore","url":"http://192.168.1.51/ISAPI/Streaming/channels/101/picture?snapShotImageType=JPEG","tls":"","persist":false,"proxy":"","authType":"digest","x":790,"y":320,"wires":[["ff3c4b90.e504a8"]]},{"id":"ff3c4b90.e504a8","type":"switch","z":"fd15b534.04ee48","name":"","property":"payload.chat.id","propertyType":"msg","rules":[{"t":"eq","v":"9999999","vt":"str"},{"t":"null"},{"t":"eq","v":"8888888","vt":"str"}],"checkall":"true","repair":false,"outputs":3,"x":950,"y":320,"wires":[["3edab718.b8c668"],["3edab718.b8c668","4295b732.a42978"],["4295b732.a42978"]],"outputLabels":["Sergey","Empty",null]},{"id":"3edab718.b8c668","type":"change","z":"fd15b534.04ee48","name":"Chat ID","rules":[{"t":"set","p":"chatId","pt":"msg","to":"99999999","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":1100,"y":300,"wires":[["d0a52ed6.209b5"]]},{"id":"d0a52ed6.209b5","type":"function","z":"fd15b534.04ee48","name":"Data","func":"context.global.keyboard = { messageId : msg.payload.messageId };\n\nvar opts = {\n reply_to_message_id: msg.payload.messageId,\n reply_markup: JSON.stringify({\n \"inline_keyboard\": [[\n {\n \"text\": \"📷 Eщё снимок\",\n \"callback_data\": \"RING MORE PIC\"\n }]\n ]\n })\n};\n\nmsg.payload = {\n chatId: msg.chatId,\n type: \"photo\",\n content: msg.payload,\n caption: \"🔔 Звонок в дверь\",\n options: opts\n};\nreturn msg;\n","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1250,"y":320,"wires":[["9a15c39c.d532"]]},{"id":"9a15c39c.d532","type":"telegram sender","z":"fd15b534.04ee48","name":"","bot":"f6e2842.9c49078","haserroroutput":false,"outputs":1,"x":1430,"y":320,"wires":[[]]},{"id":"4295b732.a42978","type":"change","z":"fd15b534.04ee48","name":"Chat ID","rules":[{"t":"set","p":"chatId","pt":"msg","to":"8888888","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":1100,"y":340,"wires":[["d0a52ed6.209b5"]]}]
Результат
Несмотря на подключение к сети через мобильного оператора, сообщение приходит в течение одной секунды.
А в Node-RED видно момент нажатия кнопки звонка, а также нажатия кнопки получения дополнительного снимка.
Kodi
Позже я обратил внимание на один из компонентов Home Assistant, который умеет слать уведомления в медиацентр Kodi.
Настроив его согласно документации, я отправился в Node-RED для обновления имеющегося сценария.
Первый делом добавил ноду call service (Kodi notify) из раздела Home Assistant.
В параметрах заполнил только поле домен, т. к. остальное легче сформировать в ноде function. Взяв этот пример скрипта за основу, я сконвертировал yaml в json и добавил в function-ноду. Затем поменял параметры по своему усмотрению.
Код этого Node-RED flow представлен ниже:
[{"id":"9f016b89.edeb18","type":"server-state-changed","z":"fd15b534.04ee48","name":"Звонок в дверь","server":"b9490f48.586ed","version":3,"exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"entityidfilter":"switch.corridor_doorbell_ding","entityidfiltertype":"substring","outputinitially":false,"state_type":"str","haltifstate":"on","halt_if_type":"str","halt_if_compare":"is","outputs":2,"output_only_on_state_change":false,"for":"","forType":"num","forUnits":"minutes","ignorePrevStateNull":false,"ignorePrevStateUnknown":false,"ignorePrevStateUnavailable":false,"ignoreCurrentStateUnknown":false,"ignoreCurrentStateUnavailable":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"eventData"},{"property":"topic","propertyType":"msg","value":"","valueType":"triggerId"}],"x":140,"y":340,"wires":[["96fbc9f0.88a2e8"],[]]},{"id":"96fbc9f0.88a2e8","type":"delay","z":"fd15b534.04ee48","name":"","pauseType":"rate","timeout":"5","timeoutUnits":"seconds","rate":"1","nbRateUnits":"120","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":true,"x":360,"y":340,"wires":[["65c28ed9.0a8e6","2618bb1b.e9dff4"]]},{"id":"65c28ed9.0a8e6","type":"stoptimer","z":"fd15b534.04ee48","duration":"1","units":"Second","payloadtype":"str","payloadval":"","name":"Timeout","x":600,"y":340,"wires":[["4a1d76bc.4213b8"],[]]},{"id":"6a886333.f832cc","type":"telegram event","z":"fd15b534.04ee48","name":"Еще снимок","bot":"f6e2842.9c49078","event":"callback_query","autoanswer":true,"x":510,"y":300,"wires":[["7d5306e4.b68658"]]},{"id":"7d5306e4.b68658","type":"switch","z":"fd15b534.04ee48","name":"","property":"payload.content","propertyType":"msg","rules":[{"t":"eq","v":"RING MORE PIC","vt":"str"}],"checkall":"true","repair":false,"outputs":1,"x":635,"y":300,"wires":[["4a1d76bc.4213b8"]],"l":false},{"id":"4a1d76bc.4213b8","type":"http request","z":"fd15b534.04ee48","name":"","method":"GET","ret":"bin","paytoqs":"ignore","url":"http://192.168.1.51/ISAPI/Streaming/channels/101/picture?snapShotImageType=JPEG","tls":"","persist":false,"proxy":"","authType":"digest","x":790,"y":320,"wires":[["ff3c4b90.e504a8"]]},{"id":"ff3c4b90.e504a8","type":"switch","z":"fd15b534.04ee48","name":"","property":"payload.chat.id","propertyType":"msg","rules":[{"t":"eq","v":"9999999","vt":"str"},{"t":"null"},{"t":"eq","v":"8888888","vt":"str"}],"checkall":"true","repair":false,"outputs":3,"x":950,"y":320,"wires":[["3edab718.b8c668"],["3edab718.b8c668","4295b732.a42978"],["4295b732.a42978"]],"outputLabels":["Sergey","Empty",null]},{"id":"3edab718.b8c668","type":"change","z":"fd15b534.04ee48","name":"Chat ID","rules":[{"t":"set","p":"chatId","pt":"msg","to":"99999999","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":1100,"y":300,"wires":[["d0a52ed6.209b5"]]},{"id":"d0a52ed6.209b5","type":"function","z":"fd15b534.04ee48","name":"Data","func":"context.global.keyboard = { messageId : msg.payload.messageId };\n\nvar opts = {\n reply_to_message_id: msg.payload.messageId,\n reply_markup: JSON.stringify({\n \"inline_keyboard\": [[\n {\n \"text\": \"📷 Eщё снимок\",\n \"callback_data\": \"RING MORE PIC\"\n }]\n ]\n })\n};\n\nmsg.payload = {\n chatId: msg.chatId,\n type: \"photo\",\n content: msg.payload,\n caption: \"🔔 Звонок в дверь\",\n options: opts\n};\nreturn msg;\n","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1250,"y":320,"wires":[["9a15c39c.d532"]]},{"id":"9a15c39c.d532","type":"telegram sender","z":"fd15b534.04ee48","name":"","bot":"f6e2842.9c49078","haserroroutput":false,"outputs":1,"x":1430,"y":320,"wires":[[]]},{"id":"4295b732.a42978","type":"change","z":"fd15b534.04ee48","name":"Chat ID","rules":[{"t":"set","p":"chatId","pt":"msg","to":"8888888","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":1100,"y":340,"wires":[["d0a52ed6.209b5"]]},{"id":"2618bb1b.e9dff4","type":"function","z":"fd15b534.04ee48","name":"Ding","func":"msg.payload = {\n service: \"kodi_livingroom_notifier\",\n data: {\n \"title\": \"Звонок в дверь\",\n \"message\": \"Звонок в дверь\",\n \"data\": {\n \"displaytime\": 10000,\n \"icon\": \"http://172.16.1.3:8123/local/images/kodi_ding.png\"\n }\n }\n}\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":550,"y":420,"wires":[["caae558f.e8dac8"]]},{"id":"caae558f.e8dac8","type":"api-call-service","z":"fd15b534.04ee48","name":"Kodi notify","server":"b9490f48.586ed","version":3,"debugenabled":false,"service_domain":"notify","service":"","entityId":"","data":"{}","dataType":"json","mergecontext":"","mustacheAltTags":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"data"}],"queue":"none","x":710,"y":420,"wires":[[]]}]
Использованные в сценарии модули Node-RED:
node-red-contrib-time-range-switch
node-red-contrib-stoptimer
node-red-contrib-telegrambot
node-red-contrib-interval
На выходе я получил дополнительное уведомление для звонка, работающего в беззвучном режиме.
Так как для использования в Kodi доступно огромное количество различных плагинов, то следом я настроил отображение видеопотока с камеры одновременно с уведомлением.
Но это тема следующих статей, так как она достаточно объемная и затрагивает
многие, еще неописанные, вещи.
В заключение скажу, что, конечно, есть отличные готовые решения.
В силу специфики своей работы или высокой стоимости они мне не подошли.
Все конфигурации, описанные в статье, доступны на GitHub.