July 15, 2021

Как сделать обычный дверной звонок умным

Сегодня я расскажу о том, как автоматизировать обычный дверной звонок, чтобы он умел работать по расписанию, в беззвучном режиме, а также слать уведомления на смартфон при нажатии на кнопку. В этом мне поможет система домашней автоматизации на базе Home Assistant.

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

Я начал с того, что определил схему подвода питания к звонку. Фаза уже присутствовала на нём, вместо того, чтобы подаваться на него в момент нажатия кнопки. В дальнейшем это сыграет свою роль.

Для реализации задуманного функционала я добавил в цепь реле. Это двухканальный релейный модуль от компании Fibaro. Работает по протоколу Z-Wave. Почему именно он, станет понятно чуть позже.

Хотя, конкретно в этом месте можно использовать любое другое устройство, будь то Fibaro, Sonoff или простой релейный модуль для Arduino.

Схема подключения

Фаза подаётся на вход реле, а из первого выхода она идёт на звонок. Нулевой провод остаётся нетронутым.

В случае, если звонок подключен так, что кнопка разрывает фазу, а не ноль,
то схема подключения будет такой — кнопка не будет работать, пока выключен выход реле O1:

Либо такой, где фаза при нажатии кнопки подается на вход реле, а звонок звенит, только, если выход О1 включен:

Программная часть

Я использую Home Assistant. Это довольно молодая, активно развивающаяся, очень мощная и гибкая система домашней автоматизации. Поддерживается огромным мировым сообществом энтузиастов, которые занимаются разработкой как самой платформы, так и компонентов, расширяющих ее функционал для работы со множеством устройств, платформ и сервисов.

Home Assistant у меня работает на Raspberry Pi 3 в Docker-контейнере. Поставляется в виде готового решения под названием Hass.io.

Также к Raspberry Pi у меня подключен Z-Wave-контроллер в виде платы расширения.

RaZberry

После подключения модуля к сети и добавления его в Home Assistant, для удобства, я переименую Z-Wave-ноду и поменяю имя и ID объекта непосредственно у реле.

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

Для работы по расписанию я покажу пример, допустим, чтобы звонок не реагировал на нажатие кнопки с 10 вечера до 9 утра.

В этом мне поможет Node-Red.

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 нужно подать фазу, то задача усложняется. Потребуется дополнительный элемент в виде твердотельного реле, управляемого переменным током.

Реле SSR-40 DA

К одному из входов для обеспечения работы этого реле подключается фаза. Эта же фаза подключается и к одному из выходов, для дальнейшей её передачи на модуль в качестве управляющего сигнала. Ноль для питания реле берём от звонка.

Таким образом, при нажатии на кнопку, реле включается, его выход закрывается,
и управляющий сигнал попадает на вход S2 модуля Fibaro.

Опять же, в случае, где кнопкой разрывается фаза, всё гораздо проще, и надо всего лишь соединить между собой общий вход реле и вход S2.

Итак, с железной частью разобрались. Перейдем к настройке.

В свойствах релейного модуля (конфигурация Z-Wave) необходимо обязательно задать два параметра. Первый отвечает за то, что при нажатой кнопке реле включено, а при отпущенной выключено.

А второй то, что кнопка является двухпозиционным выключателем. Иначе схема не будет работать.

Аналогично меняю имя и ID у переключателя, который соответствует второму реле модуля Fibaro,

и перехожу к созданию сценария для получения уведомления.

Итоговый вид сценария претерпел изменения. Код обновлённого flow доступен ниже.
  • Добавляем ноду и указываем в ней ID устройства, срабатывающего при нажатии кнопки.
  • Далее включаем ограничение получения сообщений не чаще одного раза в 2 минуты, чтобы не плодить уведомления при многократном нажатии кнопки.
  • И непосредственно отправка.

Для взаимодействия с telegram-ботом необходимо установить модуль
node-red-contrib-telegrambot для Node-RED.

  • Выбираем своего настроенного бота, в параметрах которого заполняется имя, токен, полученный при создании бота, и разрешенные для общения с ним Chat ID через запятую.
  • Указываем свой Chat ID для получения сообщений от бота и произвольный текст уведомления в виде шаблона, взятого из документации к ноде, работа методов которой, в свою очередь, основана на документации Telegram Bot API.

Снимок с камеры при звонке

Как видно, сообщение почти сразу приходит после нажатия кнопки.

Как и задумано, повторное нажатие кнопки ничего не даёт.

Дальше мне захотелось видеть звонящего. Я остановил свой выбор на аналоговой камере в виде дверного глазка.

Единственным её плюсом является возможность установки в дверь на уровне глаз. Потому как низкая стоимость камеры нивелируется необходимостью покупки видеорегистратора для получения цифрового сигнала.

Для получения снимка с камеры в уведомлении изменим сценарий, добавив ноду
HTTP request.

Итоговый вид сценария претерпел изменения. Код обновлённого flow доступен ниже.

В ней указываем адрес, который отдаёт статическое изображение (его можно взять из документации к камере или видеорегистратору).

Я заранее подключил камеру к 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 для обновления имеющегося сценария.

Итоговый вид сценария претерпел изменения. Код обновлённого flow доступен ниже.

Первый делом добавил ноду 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.