- PVSM.RU - https://www.pvsm.ru -

Доступная система видеонаблюдения с охраной на дачу за 60 минут

Речь пойдет о сборке, настройке и запуске охранной системы с датчиками, камерой, хранением видеозаписей на удаленном домашнем NAS, встроенным UPS, web-интерфейсом и оповещением через Telegram.

Чтобы быть предельно кратким, оформил статью в стилистике hackster.io. Информация рассчитана на читателей с опытом работы с одноплатным компьютером Raspberry и пониманием принципов работы VPN.

Доступная система видеонаблюдения с охраной на дачу за 60 минут - 1

Требования к системе

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

Основными требованиями были:

  • наличие охранных функций: использовать датчики в качестве пожарной сигнализации и камеру как детектор движения;

  • возможность хранить видео сроком семь дней и более; 

  • высоковольтное оборудование должно уместиться в существующем электрощитке небольшого размера и не выходить за его пределы;

  • сигнализация должна быть автономной, работать при отключении электричества;

  • минимум проводов;

  • в дальнейшем расширять функциональные возможности;

  • удаленный доступ;

  • доступность как по деньгам, так и по оборудованию.

За основу решил взять миникомпьютер Raspberry Pi Zero W.  Его легко разместить в небольшом электрощитке. Рабочий вариант выглядит так:

https://lh7-us.googleusercontent.com/7886VFf_ybeR1Z2Po0fABqVvw8bhxPYcYP8wFOt3sez0PJZJjP7izI64zcuxvO5luTDKmru_vKCzNgWnVUsIhmw9lxNWpuKhO17KY3mNq7BIiRqt6hfUbpb0jmtB1ppoJvrarbAKQ2MwMUbucdBH-hQ

Вариант установки в распределительном щите справа от электросчетчика и розетки

В итоге получилась система, которая умеет постоянно записывать видео с IP-камеры на домашний NAS через VPN, при постановке на охрану оповещать в Telegram о движении на участке и повышении температуры в доме и работать без электричества чуть более часа. Все это умещается в распределительный щит. Также можно следить за разрядом батареи,  подключаться удаленно из любой точки мира не только к камере, но и зайти на страницу управления и просмотреть состояние системы, графики изменения температуры и загрузки ЦПУ. 

https://lh7-us.googleusercontent.com/kAH1Aphxt7ydl3bPK_1EiTEtr_n01_2HuHjWvCoBDxW8U4ly63iR2ejYaMIWgpEifOtA-e4HtwbzjPWXyfBkwIxKrMhEQL_9To2s05eZg8MUaI01wb-J2u9JbkwvR9Ckt0M6dmJGjSeD3cmiKC0LgOo

Страница управления и мониторинга

Аппаратная часть

Набросал схему. Система получилась модульная, некоторые компоненты можно исключить. 

https://lh7-us.googleusercontent.com/2h2yH2ALQdtkazcKj8gP51pY3vAuirhGqzZYVKDe82_Q2xsmudHMKfo9T7vwHvPp4tnQfSrncqrg3lgtoc8sfjaJdgtf5psTdVmorcUleDqUMrmU6OyEGMQAoy8Pkk2qWQMJtH71yd1tqFziijm1Dqg

Архитектурная схема

Нам потребуется:

  • Raspberry Pi Zero W (wi-fi идет как точка доступа для раздачи интернета на даче);

  • Raspberry Pi Camera Board (используется в качестве камеры в доме);

  • Raspberry Pi Ethernet/USB HUB HAT (для подключения 4G-модема и IP-камеры);

  • UPS Lite v1.2 Power HAT [1] (в качестве источника бесперебойного питания);

  • антивандальная кнопка с подсветкой и фиксацией [2] (в качестве кнопки постановки на охрану);

  • модуль датчика температуры DS18B20 [3] (плата с датчиком температуры и встроенным резистором);

  • IP-камера с POE и дискретными входами/выходами (камера с возможностью подачи дискретного сигнала);

  • POE-адаптер 220v [4] (для подключения IP-камеры к Raspberry Pi);

  • 4G-модем (для удаленного доступа и отправки уведомлений в интернет);

  • роутер с возможностью создания VPN-сервера (для удаленного доступа);

  • NAS с приложением для видеонаблюдения (для записи и хранения видео);

  • Ethernet-кабель (для подключения камеры к Raspberry);

  • резистор (для подключения I/O-камеры к Raspberry GPIO).

Некоторые компоненты не являются обязательными и могут быть исключены. Например, можно исключить UPS Lite, если нет проблем с электричеством. Или не использовать роутер и NAS, если не требуется хранить записи удаленно. 

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

  • Raspberry Pi OS 32-bit;

  • WireGuard  (VPN-сервер на роутере и VPN-клиент на raspberry);

  • Telegram;

  • Node-RED (инструмент потокового программирования):

Сборка

https://lh7-us.googleusercontent.com/Itm9EEKK6jI2RQa5YzjK6Qug69NNobE8ZoEsfuTgjnltQiua5LyK40CMviML_HPVR1BH7DPRd_7zlOl19SqMKz_7asRMR5htHE5N_WvxRtNFHbtfkcqZQl4yb6h3dr4QLyITfpA9b192hRM-rbbtAqQ

Собираем «бутерброд». Подключаем Raspberry Pi Cam к Raspberry Pi Zero W. Снизу подсоединяем UPS Lite, сверху - Raspberry Pi Ethernet/USB HUB HAT. Добавляем датчик  температуры DS18B20. Я использовал модуль из набора для Arduino, в нем кроме датчика температуры уже добавлен резистор и выведены контакты для быстрого подключения.  Помним, что этот датчик использует 1-Wire  протокол, на контроллере это пин GPIO4.

Подключаем согласно архитектурной схеме представленной выше: 

  • минус заводим на пин GND;

  • плюс заводим на пин 3.3v;

  • сигнальный вывод на пин GPIO4.

Подключаем ввод-вывод IP-камеры к Raspberry. Я использовал камеру с наличием ввода-вывода, так как считаю этот способ более надежным и гибким в конфигурировании. Принцип следующий: как только камера обнаруживает движение или попытки взлома, формирует на выходе сигнал. В зависимости от настроек он может быть как постоянным, так и импульсным, а контакт - нормально открытым или нормально замкнутым. В моем случае у камеры на выходе будет постоянное напряжение при нормально замкнутом контакте. У Raspberry Pi Zero напряжение на пинах ввода-вывода 3.3v для логической единицы и 0v для нуля. У меня уже была камера Geovision, в описании которой сказано, что максимальный ток 30mA и на дискретном выходе 5v, следовательно мне подойдет резистор на 100 Ом. Возвращаемся к схеме и подключаем:

  • плюс с камеры на резистор, с резистора на пин GPIO27;

  • минус с камеры заводим на пин GND.

Теперь кнопка постановки на охрану. Я выбирал кнопку с LED-индикацией. Это позволяет визуально оценить, включена охрана или нет. Также индикация меняется при обнаружении движения. Удобно то, что кнопка рассчитана на питание 3-6V, что существенно облегчает монтаж к Raspberry Pi. Кнопка имеет пять контактов и позволяет подсоединить ее разными способами. Я выбрал тот, при котором подсветка горит при нажатой кнопке. Подключение сделал следующим образом:

  • GPIO17 подключил к кнопке к контакту С;

  • минус кнопки заводим на пин GND;

  • контакт  кнопки NO подключил к GPIO22;

  • минус кнопки заводим на пин GND.

https://lh7-us.googleusercontent.com/PQtZ1quK-h6LIpMmu-Na8uAN0IRQ_fF7LPMCo037FYwmbPeh3ba8GzWqHQ4KIPgUje5GnFI7lXSPDMlZZoHye2d6sj1m_MRycXj__7n8FWz06cyJIcUmpk_hgasCGUGQODiEuby1Y3JUurxACqTlz6E

Дело за малым - подключить к USB-порту LTE-модем, Ethernet-кабель от Raspberry Pi Ethernet/USB HUB HAT к POE-адаптеру, а от адаптера к IP-камере. Запитать UPS Lite от стандартного блока питания для Raspberry и включить POE-адаптер в розетку. Как все это выглядит живьем, можно посмотреть на самом первом фото.

Настройка

Установка и настройка Raspberry Pi

Подготавливаем Raspberry Pi к работе. Первым делом установим Raspberry Pi OS 32-bit. Установка  подробно описана на официальном сайте [13]. После успешной установки и загрузки OS включаем поддержку протокола 1-Wire и активируем Raspberry Pi Camera Board. Делаем это через утилиту raspi-config запуском в терминале команды:

sudo raspi-config

В открывшемся окне настроек переходим в раздел Interfacing options.

https://lh7-us.googleusercontent.com/Tn_XgWZwCKHAaOHiR8ebWkXBd0rkcETdLVckZXpP8Db58Oblr22Qi5I5RWZKDdloc5rsAsLf0AqvU3Uhj3nd95hyh61B2xxXI_J7xbJkYugrAIjtsveLSEefPJJgUqzvolbEVu0umlvMM883xfl379U

Активируем P1 Camera и 1-Wire.

https://lh7-us.googleusercontent.com/4cF7K0VkC0yA0lCIMzbXdwiW1EggbFDZ5YGplOEc9gE_V3Lh4YKV-iwIHLRvWJgXB__APt-NcYv7rEa7jxMlTSqE1xiUtro41qnQU8UE6lUFcTbyjIotF6fxR6cKo6iM4m4_rh-0xK4FFSADlutXc3I

После сохранения настроек, скорее всего, понадобится перезагрузить Raspberry Pi.

Установка Node-RED

Далее устанавливаем инструмент потокового программирования Node-RED. На Хабре много статей о самом продукте и его установке. В моем случае никаких особенностей нет, выполняем команду:

$ bash <(curl -sL https://raw.githubusercontent.com/node-red/linux-installers/master/deb/update-nodejs-and-nodered)

После успешного выполнения скрипта,  устанавливаем модули:

  1. node-red-contrib-boolean-logic
    $ npm install node-red-contrib-boolean-logic

  2. node-red-contrib-camerapi
    $ npm install node-red-contrib-camerapi

  3. node-red-contrib-cpu
    $ npm install node-red-contrib-cpu

  4. node-red-contrib-easybotics-ina219-sensor
    $ npm install node-red-contrib-easybotics-ina219-sensor

  5. node-red-contrib-edge-trigger
    $ npm install node-red-contrib-edge-trigger

  6. node-red-contrib-sensor-ds18b20
    $ npm install node-red-contrib-sensor-ds18b20

  7. node-red-contrib-telegrambot
    $ npm install node-red-contrib-telegrambot

  8. node-red-node-pi-gpio
    $ npm install node-red-node-pi-gpio

Включаем автозапуск и запускаем Node-RED.

$ sudo systemctl enable nodered.service
$ sudo systemctl start nodered.service

Открываем редактор

Как только Node-RED запустится, подключаемся через браузер по адресу: http://<ip raspberry>:1880.  

https://lh7-us.googleusercontent.com/6DN9wcKfTXPZC5n6lXnyPy77qC60bXPmE83OskVHLwsnEpCMMv4Gdw75jsViDjSf8jGFzvendFnV4UJu1R-5Jdd7Mj9Eu6QuqKKIM3qKjbJGY-E5aYV2ZooAXpKR9xmm-a7opg9zkUDYE-ifVDbPsSs

Подготавливаем проект под себя

Проект представляет собой json-файл. Так как для отправки уведомлений используется модуль Telegram, в настройках которого указывается id Telegram-канала, в который отправляются оповещения, то понадобиться зарегистрировать Telegram-бота и создать чат. Для регистрации бота воспользуйтесь служебным ботом @BotFather. Узнать id-номер чата поможет бот @getmyid_bot, добавленный в созданный Telegram-чат. Чат позволит добавить пользователей, которые будут получать сообщения от системы. Номер созданного чата необходимо подставить в json-файле вместо текста  CHANGE_CHAT_ID. 

Содержимое json-файл проекта
[
	{
    	"id": "35700ef4.b4cc62",
    	"type": "tab",
    	"label": "Dacha",
    	"disabled": false,
    	"info": ""
	},
	{
    	"id": "21ee0549ab80c1b3",
    	"type": "group",
    	"z": "35700ef4.b4cc62",
    	"name": "Temperature external",
    	"style": {
        	"fill": "#c8e7a7",
        	"label": true,
        	"stroke": "#000000",
        	"color": "#000000"
    	},
    	"nodes": [
        	"1b33f0bb.ddc0af",
        	"8cb14a63.63e588",
        	"bf42d115.5f8dc",
        	"f826e2b9.18e4c",
        	"37299a0b.442dc6",
        	"589f8077.70b05",
        	"7744d782.93a8f8",
        	"cd44f017.139e6",
        	"d9e7f0b2.9c11a"
    	],
    	"x": 34,
    	"y": 479,
    	"w": 1172,
    	"h": 302
	},
	{
    	"id": "3a19bea8.e27032",
    	"type": "group",
    	"z": "35700ef4.b4cc62",
    	"name": "Camera external motion alarm",
    	"style": {
        	"stroke": "#000000",
        	"fill": "#c8e7a7",
        	"label": true,
        	"color": "#000000"
    	},
    	"nodes": [
        	"5d05a379.500dcc",
        	"636c36b0.9db488",
        	"391ad295.c56a6e",
        	"14425883.8a8027",
        	"c14e914c.160e1",
        	"57894684.68d3c8",
        	"f7c59ddc.2f24c",
        	"c411c632.3dd2f8",
        	"48255a26.189904",
        	"78cfa48.1eae25c",
        	"ce092f2e.fb6ff",
        	"3b1e904c.649e9",
        	"48d31990.641c08",
        	"1d8f6830.1c7338",
        	"7f12a359.8f4dfc",
        	"6299cce8.7b20a4",
        	"7f956fcc.36206",
        	"e9b9f2a1a8421187",
        	"548c306e6e2c111f"
    	],
    	"x": 34,
    	"y": 19,
    	"w": 1192,
    	"h": 422
	},
	{
    	"id": "6e2ba88d.60c498",
    	"type": "group",
    	"z": "35700ef4.b4cc62",
    	"name": "CPU Load and temperature",
    	"style": {
        	"stroke": "#000000",
        	"fill": "#c8e7a7",
        	"label": true,
        	"color": "#000000"
    	},
    	"nodes": [
        	"b8c052bb.44535",
        	"d6fd679.6caa398",
        	"dab61cbf.cfa87",
        	"fdb3a1b6.cfb71",
        	"a0e62b1a.757c48"
    	],
    	"x": 34,
    	"y": 819,
    	"w": 452,
    	"h": 282
	},
	{
    	"id": "72f5dbe4bc2de2b5",
    	"type": "group",
    	"z": "35700ef4.b4cc62",
    	"name": "Capture photo",
    	"style": {
        	"fill": "#c8e7a7",
        	"label": true,
        	"stroke": "#000000",
        	"color": "#000000"
    	},
    	"nodes": [
        	"9aca7759.d95a48",
        	"9ca8d5ff.3857d8",
        	"24ee77ab.2e8738",
        	"ea0b75b0.94f058",
        	"c452f057.ca63a"
    	],
    	"x": 44,
    	"y": 1159,
    	"w": 1182,
    	"h": 142
	},
	{
    	"id": "8c22d8c9580d2858",
    	"type": "group",
    	"z": "35700ef4.b4cc62",
    	"name": "",
    	"style": {
        	"label": true,
        	"fill": "#c8e7a7"
    	},
    	"nodes": [
        	"cc5bc2c3a2c509fa",
        	"e291c889c1d4e186",
        	"0d8935ca87af5a1b",
        	"a067feaf7d06fabf",
        	"2686f0fe374cc52d",
        	"d6992fd1eb26d528",
        	"589c720b5a2be2f2",
        	"4ecdc496c0ecd286",
        	"b9d28942538208e4",
        	"bd842c6db6853e79",
        	"26d9630fb660bc9d",
        	"0d4f374253a9f8d4",
        	"65fe44dc0bb4ca1d"
    	],
    	"x": 514,
    	"y": 819,
    	"w": 692,
    	"h": 282
	},
	{
    	"id": "9aca7759.d95a48",
    	"type": "camerapi-takephoto",
    	"z": "35700ef4.b4cc62",
    	"g": "72f5dbe4bc2de2b5",
    	"filemode": "1",
    	"filename": "photo1.JPEG",
    	"filedefpath": "0",
    	"filepath": "/home/pi/Pictures/",
    	"fileformat": "jpeg",
    	"resolution": "3",
    	"rotation": "0",
    	"fliph": "0",
    	"flipv": "0",
    	"brightness": "50",
    	"contrast": "0",
    	"sharpness": "0",
    	"quality": "80",
    	"imageeffect": "none",
    	"exposuremode": "auto",
    	"iso": "0",
    	"agcwait": "1.0",
    	"led": "0",
    	"awb": "auto",
    	"name": "",
    	"x": 860,
    	"y": 1260,
    	"wires": [
        	[
            	"24ee77ab.2e8738",
            	"ea0b75b0.94f058"
        	]
    	]
	},
	{
    	"id": "9ca8d5ff.3857d8",
    	"type": "ui_template",
    	"z": "35700ef4.b4cc62",
    	"g": "72f5dbe4bc2de2b5",
    	"group": "fddd6117.b012f",
    	"name": "",
    	"order": 1,
    	"width": "10",
    	"height": "13",
    	"format": "<script>nvar value = "1";n// or overwrite value in your callback function ...nthis.scope.action = function() { return value; }nnfunction updateF() {n  var source = '/photo1.JPEG',n  timestamp = (new Date()).getTime(),n  newUrl = source + '?_=' + timestamp;n  document.getElementById("photo").src = newUrl;n}n</script>nn<md-button ng-click="send({payload:action()})" onclick="setTimeout(updateF, 2500);" style="padding:40px; margin-bottom: 40px; color: #000000; background-color: #888981;" >n <ui-icon icon="camera"></ui-icon>n Take a photo<br>n</md-button>nn<div style="margin-bottom:40px;">n <img src="/photo1.JPEG" id="photo" width="100%" height="100%">n</div>",
    	"storeOutMessages": true,
    	"fwdInMessages": true,
    	"resendOnRefresh": true,
    	"templateScope": "local",
    	"x": 130,
    	"y": 1260,
    	"wires": [
        	[
            	"9aca7759.d95a48"
        	]
    	]
	},
	{
    	"id": "24ee77ab.2e8738",
    	"type": "debug",
    	"z": "35700ef4.b4cc62",
    	"g": "72f5dbe4bc2de2b5",
    	"name": "",
    	"active": true,
    	"tosidebar": true,
    	"console": false,
    	"tostatus": false,
    	"complete": "false",
    	"statusVal": "",
    	"statusType": "auto",
    	"x": 1110,
    	"y": 1260,
    	"wires": []
	},
	{
    	"id": "5d05a379.500dcc",
    	"type": "inject",
    	"z": "35700ef4.b4cc62",
    	"g": "3a19bea8.e27032",
    	"name": "Test sms",
    	"props": [
        	{
            	"p": "payload"
        	},
        	{
            	"p": "topic",
            	"vt": "str"
        	}
    	],
    	"repeat": "",
    	"crontab": "",
    	"once": false,
    	"onceDelay": 0.1,
    	"topic": "",
    	"payload": "",
    	"payloadType": "date",
    	"x": 760,
    	"y": 340,
    	"wires": [
        	[
            	"e9b9f2a1a8421187"
        	]
    	]
	},
	{
    	"id": "636c36b0.9db488",
    	"type": "function",
    	"z": "35700ef4.b4cc62",
    	"g": "3a19bea8.e27032",
    	"name": "",
    	"func": "msg.payload = {}nmsg.payload.chatId = CHANGE_CHAT_IDnmsg.payload.type = "message"nmsg.payload.content = "Motion detected! [myDacha]"nreturn msg;n",
    	"outputs": 1,
    	"noerr": 0,
    	"initialize": "",
    	"finalize": "",
    	"libs": [],
    	"x": 980,
    	"y": 320,
    	"wires": [
        	[
            	"f5b3043a.2a3ff8"
        	]
    	]
	},
	{
    	"id": "391ad295.c56a6e",
    	"type": "rpi-gpio in",
    	"z": "35700ef4.b4cc62",
    	"g": "3a19bea8.e27032",
    	"name": "camera grpio 7",
    	"pin": "13",
    	"intype": "down",
    	"debounce": "25",
    	"read": true,
    	"x": 140,
    	"y": 220,
    	"wires": [
        	[
            	"f7c59ddc.2f24c",
            	"78cfa48.1eae25c",
            	"48d31990.641c08",
            	"548c306e6e2c111f"
        	]
    	]
	},
	{
    	"id": "14425883.8a8027",
    	"type": "rising-edge",
    	"z": "35700ef4.b4cc62",
    	"g": "3a19bea8.e27032",
    	"name": "",
    	"threshold": "0",
    	"x": 770,
    	"y": 280,
    	"wires": [
        	[
            	"636c36b0.9db488"
        	]
    	]
	},
	{
    	"id": "c14e914c.160e1",
    	"type": "ui_button",
    	"z": "35700ef4.b4cc62",
    	"g": "3a19bea8.e27032",
    	"name": "",
    	"group": "371d08d2.682078",
    	"order": 3,
    	"width": "6",
    	"height": "1",
    	"passthru": false,
    	"label": "Test msg",
    	"tooltip": "",
    	"color": "#000000",
    	"bgcolor": "#888981",
    	"icon": "",
    	"payload": "1",
    	"payloadType": "str",
    	"topic": "topic",
    	"topicType": "msg",
    	"x": 760,
    	"y": 400,
    	"wires": [
        	[
            	"e9b9f2a1a8421187"
        	]
    	]
	},
	{
    	"id": "57894684.68d3c8",
    	"type": "ui_switch",
    	"z": "35700ef4.b4cc62",
    	"g": "3a19bea8.e27032",
    	"name": "",
    	"label": "Лампочка",
    	"tooltip": "",
    	"group": "371d08d2.682078",
    	"order": 1,
    	"width": "6",
    	"height": "1",
    	"passthru": true,
    	"decouple": "false",
    	"topic": "topic",
    	"topicType": "msg",
    	"style": "",
    	"onvalue": "true",
    	"onvalueType": "bool",
    	"onicon": "",
    	"oncolor": "",
    	"offvalue": "false",
    	"offvalueType": "bool",
    	"officon": "",
    	"offcolor": "",
    	"animate": false,
    	"x": 1100,
    	"y": 60,
    	"wires": [
        	[
            	"316ad35.6698d2c"
        	]
    	]
	},
	{
    	"id": "f7c59ddc.2f24c",
    	"type": "ui_text",
    	"z": "35700ef4.b4cc62",
    	"g": "3a19bea8.e27032",
    	"group": "189e2e48.d109d2",
    	"order": 1,
    	"width": 0,
    	"height": 0,
    	"name": "",
    	"label": "Движение",
    	"format": "{{msg.payload}}",
    	"layout": "row-spread",
    	"x": 310,
    	"y": 300,
    	"wires": []
	},
	{
    	"id": "c411c632.3dd2f8",
    	"type": "ui_text",
    	"z": "35700ef4.b4cc62",
    	"g": "3a19bea8.e27032",
    	"group": "189e2e48.d109d2",
    	"order": 3,
    	"width": 0,
    	"height": 0,
    	"name": "",
    	"label": "Охрана",
    	"format": "{{msg.payload}}",
    	"layout": "row-spread",
    	"x": 300,
    	"y": 60,
    	"wires": []
	},
	{
    	"id": "48255a26.189904",
    	"type": "rpi-gpio in",
    	"z": "35700ef4.b4cc62",
    	"g": "3a19bea8.e27032",
    	"name": "Security Button",
    	"pin": "15",
    	"intype": "up",
    	"debounce": "250",
    	"read": true,
    	"x": 140,
    	"y": 120,
    	"wires": [
        	[
            	"c411c632.3dd2f8",
            	"78cfa48.1eae25c",
            	"3b1e904c.649e9"
        	]
    	]
	},
	{
    	"id": "78cfa48.1eae25c",
    	"type": "BooleanLogic",
    	"z": "35700ef4.b4cc62",
    	"g": "3a19bea8.e27032",
    	"name": "",
    	"operation": "AND",
    	"inputCount": "2",
    	"topic": "result",
    	"x": 500,
    	"y": 220,
    	"wires": [
        	[
            	"14425883.8a8027",
            	"6299cce8.7b20a4"
        	]
    	]
	},
	{
    	"id": "ce092f2e.fb6ff",
    	"type": "inject",
    	"z": "35700ef4.b4cc62",
    	"g": "3a19bea8.e27032",
    	"name": "Blink 1",
    	"props": [
        	{
            	"p": "payload"
        	},
        	{
            	"p": "topic",
            	"vt": "str"
        	}
    	],
    	"repeat": "0.5",
    	"crontab": "",
    	"once": true,
    	"onceDelay": "0.5",
    	"topic": "",
    	"payload": "true",
    	"payloadType": "bool",
    	"x": 480,
    	"y": 60,
    	"wires": [
        	[
            	"3b1e904c.649e9"
        	]
    	]
	},
	{
    	"id": "3b1e904c.649e9",
    	"type": "BooleanLogic",
    	"z": "35700ef4.b4cc62",
    	"g": "3a19bea8.e27032",
    	"name": "",
    	"operation": "AND",
    	"inputCount": "3",
    	"topic": "result",
    	"x": 720,
    	"y": 120,
    	"wires": [
        	[
            	"7f12a359.8f4dfc"
        	]
    	]
	},
	{
    	"id": "1b33f0bb.ddc0af",
    	"type": "inject",
    	"z": "35700ef4.b4cc62",
    	"g": "21ee0549ab80c1b3",
    	"name": "",
    	"props": [
        	{
            	"p": "payload"
        	},
        	{
            	"p": "topic",
            	"vt": "str"
        	}
    	],
    	"repeat": "1",
    	"crontab": "",
    	"once": false,
    	"onceDelay": 0.1,
    	"topic": "",
    	"payload": "",
    	"payloadType": "date",
    	"x": 170,
    	"y": 640,
    	"wires": [
        	[
            	"8cb14a63.63e588"
        	]
    	]
	},
	{
    	"id": "8cb14a63.63e588",
    	"type": "sensor-ds18b20",
    	"z": "35700ef4.b4cc62",
    	"g": "21ee0549ab80c1b3",
    	"name": "",
    	"topic": "",
    	"sensorid": "28-011632edb7ee",
    	"timer": "1",
    	"repeat": false,
    	"x": 390,
    	"y": 640,
    	"wires": [
        	[
            	"bf42d115.5f8dc",
            	"cd44f017.139e6",
            	"d9e7f0b2.9c11a"
        	]
    	]
	},
	{
    	"id": "bf42d115.5f8dc",
    	"type": "switch",
    	"z": "35700ef4.b4cc62",
    	"g": "21ee0549ab80c1b3",
    	"name": "Above 40",
    	"property": "payload",
    	"propertyType": "msg",
    	"rules": [
        	{
            	"t": "gte",
            	"v": "40",
            	"vt": "str"
        	}
    	],
    	"checkall": "true",
    	"repair": false,
    	"outputs": 1,
    	"x": 600,
    	"y": 520,
    	"wires": [
        	[
            	"37299a0b.442dc6"
        	]
    	]
	},
	{
    	"id": "f826e2b9.18e4c",
    	"type": "function",
    	"z": "35700ef4.b4cc62",
    	"g": "21ee0549ab80c1b3",
    	"name": "",
    	"func": "msg.payload = {}nmsg.payload.chatId = CHANGE_CHAT_IDnmsg.payload.type = "message"nmsg.payload.content = "Temperature HI! [myDacha]"nreturn msg;",
    	"outputs": 1,
    	"noerr": 0,
    	"initialize": "",
    	"finalize": "",
    	"libs": [],
    	"x": 1120,
    	"y": 520,
    	"wires": [
        	[
            	"f5b3043a.2a3ff8"
        	]
    	]
	},
	{
    	"id": "37299a0b.442dc6",
    	"type": "trigger",
    	"z": "35700ef4.b4cc62",
    	"g": "21ee0549ab80c1b3",
    	"name": "",
    	"op1": "1",
    	"op2": "0",
    	"op1type": "str",
    	"op2type": "str",
    	"duration": "5",
    	"extend": true,
    	"overrideDelay": false,
    	"units": "s",
    	"reset": "",
    	"bytopic": "all",
    	"topic": "topic",
    	"outputs": 1,
    	"x": 770,
    	"y": 520,
    	"wires": [
        	[
            	"7744d782.93a8f8"
        	]
    	]
	},
	{
    	"id": "589f8077.70b05",
    	"type": "debug",
    	"z": "35700ef4.b4cc62",
    	"g": "21ee0549ab80c1b3",
    	"name": "",
    	"active": true,
    	"tosidebar": true,
    	"console": false,
    	"tostatus": false,
    	"complete": "payload",
    	"targetType": "msg",
    	"statusVal": "",
    	"statusType": "auto",
    	"x": 670,
    	"y": 600,
    	"wires": []
	},
	{
    	"id": "7744d782.93a8f8",
    	"type": "switch",
    	"z": "35700ef4.b4cc62",
    	"g": "21ee0549ab80c1b3",
    	"name": "",
    	"property": "payload",
    	"propertyType": "msg",
    	"rules": [
        	{
            	"t": "eq",
            	"v": "1",
            	"vt": "str"
        	}
    	],
    	"checkall": "true",
    	"repair": false,
    	"outputs": 1,
    	"x": 950,
    	"y": 520,
    	"wires": [
        	[
            	"f826e2b9.18e4c"
        	]
    	]
	},
	{
    	"id": "cd44f017.139e6",
    	"type": "ui_gauge",
    	"z": "35700ef4.b4cc62",
    	"g": "21ee0549ab80c1b3",
    	"name": "",
    	"group": "189e2e48.d109d2",
    	"order": 5,
    	"width": "6",
    	"height": "6",
    	"gtype": "gage",
    	"title": "Temperature ext",
    	"label": "units",
    	"format": "{{value}}",
    	"min": "-40",
    	"max": "60",
    	"colors": [
        	"#00b500",
        	"#e6e600",
        	"#ca3838"
    	],
    	"seg1": "",
    	"seg2": "",
    	"x": 860,
    	"y": 680,
    	"wires": []
	},
	{
    	"id": "d9e7f0b2.9c11a",
    	"type": "ui_chart",
    	"z": "35700ef4.b4cc62",
    	"g": "21ee0549ab80c1b3",
    	"name": "",
    	"group": "172f5cc2.238383",
    	"order": 1,
    	"width": "18",
    	"height": "4",
    	"label": "Temperature ext",
    	"chartType": "line",
    	"legend": "false",
    	"xformat": "HH:mm:ss",
    	"interpolate": "linear",
    	"nodata": "",
    	"dot": false,
    	"ymin": "-40",
    	"ymax": "60",
    	"removeOlder": "1",
    	"removeOlderPoints": "",
    	"removeOlderUnit": "3600",
    	"cutout": 0,
    	"useOneColor": false,
    	"useUTC": false,
    	"colors": [
        	"#3f16d4",
        	"#aec7e8",
        	"#ff7f0e",
        	"#2ca02c",
        	"#98df8a",
        	"#d62728",
        	"#ff9896",
        	"#9467bd",
        	"#c5b0d5"
    	],
    	"outputs": 1,
    	"useDifferentColor": false,
    	"x": 860,
    	"y": 740,
    	"wires": [
        	[]
    	]
	},
	{
    	"id": "ea0b75b0.94f058",
    	"type": "function",
    	"z": "35700ef4.b4cc62",
    	"g": "72f5dbe4bc2de2b5",
    	"name": "",
    	"func": "var payload = { nchatId: CHANGE_CHAT_ID,n	type: "photo",n	content: "/home/pi/Pictures/photo1.JPEG" };nreturn {payload};",
    	"outputs": 1,
    	"noerr": 0,
    	"initialize": "",
    	"finalize": "",
    	"libs": [],
    	"x": 1100,
    	"y": 1200,
    	"wires": [
        	[
            	"f5b3043a.2a3ff8"
        	]
    	]
	},
	{
    	"id": "c452f057.ca63a",
    	"type": "inject",
    	"z": "35700ef4.b4cc62",
    	"g": "72f5dbe4bc2de2b5",
    	"name": "Test web cam img",
    	"props": [
        	{
            	"p": "payload"
        	}
    	],
    	"repeat": "",
    	"crontab": "",
    	"once": false,
    	"onceDelay": 0.1,
    	"topic": "",
    	"payload": "1",
    	"payloadType": "num",
    	"x": 850,
    	"y": 1200,
    	"wires": [
        	[
            	"ea0b75b0.94f058"
        	]
    	]
	},
	{
    	"id": "b8c052bb.44535",
    	"type": "cpu",
    	"z": "35700ef4.b4cc62",
    	"g": "6e2ba88d.60c498",
    	"name": "",
    	"msgCore": true,
    	"msgOverall": false,
    	"msgArray": false,
    	"msgTemp": false,
    	"x": 170,
    	"y": 860,
    	"wires": [
        	[
            	"d6fd679.6caa398"
        	]
    	]
	},
	{
    	"id": "d6fd679.6caa398",
    	"type": "ui_chart",
    	"z": "35700ef4.b4cc62",
    	"g": "6e2ba88d.60c498",
    	"name": "",
    	"group": "172f5cc2.238383",
    	"order": 3,
    	"width": "18",
    	"height": "4",
    	"label": "CPU usage",
    	"chartType": "line",
    	"legend": "true",
    	"xformat": "HH:mm:ss",
    	"interpolate": "linear",
    	"nodata": "",
    	"dot": false,
    	"ymin": "0",
    	"ymax": "100",
    	"removeOlder": "1",
    	"removeOlderPoints": "",
    	"removeOlderUnit": "3600",
    	"cutout": 0,
    	"useOneColor": false,
    	"useUTC": false,
    	"colors": [
        	"#b33c1e",
        	"#aec7e8",
        	"#ff7f0e",
        	"#2ca02c",
        	"#98df8a",
        	"#d62728",
        	"#ff9896",
        	"#9467bd",
        	"#c5b0d5"
    	],
    	"outputs": 1,
    	"useDifferentColor": false,
    	"x": 390,
    	"y": 880,
    	"wires": [
        	[]
    	]
	},
	{
    	"id": "dab61cbf.cfa87",
    	"type": "inject",
    	"z": "35700ef4.b4cc62",
    	"g": "6e2ba88d.60c498",
    	"name": "",
    	"props": [
        	{
            	"p": "payload"
        	},
        	{
            	"p": "topic",
            	"vt": "str"
        	}
    	],
    	"repeat": "1",
    	"crontab": "",
    	"once": true,
    	"onceDelay": "1",
    	"topic": "",
    	"payload": "",
    	"payloadType": "date",
    	"x": 170,
    	"y": 960,
    	"wires": [
        	[
            	"b8c052bb.44535",
            	"fdb3a1b6.cfb71"
        	]
    	]
	},
	{
    	"id": "fdb3a1b6.cfb71",
    	"type": "cpu",
    	"z": "35700ef4.b4cc62",
    	"g": "6e2ba88d.60c498",
    	"name": "CPU Temp",
    	"msgCore": false,
    	"msgOverall": false,
    	"msgArray": false,
    	"msgTemp": true,
    	"x": 170,
    	"y": 1060,
    	"wires": [
        	[
            	"a0e62b1a.757c48"
        	]
    	]
	},
	{
    	"id": "a0e62b1a.757c48",
    	"type": "ui_chart",
    	"z": "35700ef4.b4cc62",
    	"g": "6e2ba88d.60c498",
    	"name": "",
    	"group": "172f5cc2.238383",
    	"order": 5,
    	"width": "18",
    	"height": "4",
    	"label": "CPU Temp",
    	"chartType": "line",
    	"legend": "false",
    	"xformat": "HH:mm:ss",
    	"interpolate": "linear",
    	"nodata": "",
    	"dot": false,
    	"ymin": "",
    	"ymax": "",
    	"removeOlder": "1",
    	"removeOlderPoints": "",
    	"removeOlderUnit": "3600",
    	"cutout": 0,
    	"useOneColor": false,
    	"useUTC": false,
    	"colors": [
        	"#30b31e",
        	"#aec7e8",
        	"#ff7f0e",
        	"#2ca02c",
        	"#98df8a",
        	"#d62728",
        	"#ff9896",
        	"#9467bd",
        	"#c5b0d5"
    	],
    	"outputs": 1,
    	"useDifferentColor": false,
    	"x": 370,
    	"y": 1020,
    	"wires": [
        	[]
    	]
	},
	{
    	"id": "f5b3043a.2a3ff8",
    	"type": "telegram sender",
    	"z": "35700ef4.b4cc62",
    	"name": "",
    	"bot": "d9de0dcc.3d2a6",
    	"haserroroutput": false,
    	"outputs": 1,
    	"x": 1430,
    	"y": 320,
    	"wires": [
        	[]
    	]
	},
	{
    	"id": "316ad35.6698d2c",
    	"type": "rpi-gpio out",
    	"z": "35700ef4.b4cc62",
    	"name": "LED",
    	"pin": "11",
    	"set": true,
    	"level": "0",
    	"freq": "",
    	"out": "out",
    	"x": 1330,
    	"y": 140,
    	"wires": []
	},
	{
    	"id": "48d31990.641c08",
    	"type": "Invert",
    	"z": "35700ef4.b4cc62",
    	"g": "3a19bea8.e27032",
    	"name": "Invert",
    	"x": 490,
    	"y": 160,
    	"wires": [
        	[
            	"3b1e904c.649e9"
        	]
    	]
	},
	{
    	"id": "1d8f6830.1c7338",
    	"type": "trigger",
    	"z": "35700ef4.b4cc62",
    	"g": "3a19bea8.e27032",
    	"name": "",
    	"op1": "",
    	"op2": "false",
    	"op1type": "pay",
    	"op2type": "str",
    	"duration": "250",
    	"extend": false,
    	"overrideDelay": false,
    	"units": "ms",
    	"reset": "",
    	"bytopic": "all",
    	"topic": "topic",
    	"outputs": 1,
    	"x": 1120,
    	"y": 120,
    	"wires": [
        	[
            	"316ad35.6698d2c"
        	]
    	]
	},
	{
    	"id": "7f12a359.8f4dfc",
    	"type": "switch",
    	"z": "35700ef4.b4cc62",
    	"g": "3a19bea8.e27032",
    	"name": "",
    	"property": "payload",
    	"propertyType": "msg",
    	"rules": [
        	{
            	"t": "eq",
            	"v": "1",
            	"vt": "str"
        	}
    	],
    	"checkall": "true",
    	"repair": false,
    	"outputs": 1,
    	"x": 890,
    	"y": 120,
    	"wires": [
        	[
            	"1d8f6830.1c7338"
        	]
    	]
	},
	{
    	"id": "6299cce8.7b20a4",
    	"type": "switch",
    	"z": "35700ef4.b4cc62",
    	"g": "3a19bea8.e27032",
    	"name": "",
    	"property": "payload",
    	"propertyType": "msg",
    	"rules": [
        	{
            	"t": "eq",
            	"v": "1",
            	"vt": "str"
        	}
    	],
    	"checkall": "true",
    	"repair": false,
    	"outputs": 1,
    	"x": 770,
    	"y": 200,
    	"wires": [
        	[
            	"7f956fcc.36206"
        	]
    	]
	},
	{
    	"id": "7f956fcc.36206",
    	"type": "trigger",
    	"z": "35700ef4.b4cc62",
    	"g": "3a19bea8.e27032",
    	"name": "",
    	"op1": "",
    	"op2": "true",
    	"op1type": "pay",
    	"op2type": "str",
    	"duration": "100",
    	"extend": false,
    	"overrideDelay": false,
    	"units": "ms",
    	"reset": "",
    	"bytopic": "all",
    	"topic": "topic",
    	"outputs": 1,
    	"x": 1020,
    	"y": 200,
    	"wires": [
        	[
            	"316ad35.6698d2c"
        	]
    	]
	},
	{
    	"id": "cc5bc2c3a2c509fa",
    	"type": "ina-sensor",
    	"z": "35700ef4.b4cc62",
    	"g": "8c22d8c9580d2858",
    	"name": "",
    	"handle": "93c1182fe25c4f4a",
    	"x": 670,
    	"y": 940,
    	"wires": [
        	[
            	"e291c889c1d4e186",
            	"0d4f374253a9f8d4"
        	],
        	[
            	"0d8935ca87af5a1b",
            	"26d9630fb660bc9d"
        	]
    	]
	},
	{
    	"id": "65fe44dc0bb4ca1d",
    	"type": "inject",
    	"z": "35700ef4.b4cc62",
    	"g": "8c22d8c9580d2858",
    	"name": "",
    	"props": [
        	{
            	"p": "payload"
        	},
        	{
            	"p": "topic",
            	"vt": "str"
        	}
    	],
    	"repeat": "1",
    	"crontab": "",
    	"once": false,
    	"onceDelay": 0.1,
    	"topic": "",
    	"payloadType": "date",
    	"x": 650,
    	"y": 880,
    	"wires": [
        	[
            	"cc5bc2c3a2c509fa"
        	]
    	]
	},
	{
    	"id": "e291c889c1d4e186",
    	"type": "switch",
    	"z": "35700ef4.b4cc62",
    	"g": "8c22d8c9580d2858",
    	"name": "Less 3.5v",
    	"property": "payload",
    	"propertyType": "msg",
    	"rules": [
        	{
            	"t": "lt",
            	"v": "3.5",
            	"vt": "str"
        	}
    	],
    	"checkall": "true",
    	"repair": false,
    	"outputs": 1,
    	"x": 900,
    	"y": 900,
    	"wires": [
        	[
            	"d6992fd1eb26d528"
        	]
    	]
	},
	{
    	"id": "0d8935ca87af5a1b",
    	"type": "switch",
    	"z": "35700ef4.b4cc62",
    	"g": "8c22d8c9580d2858",
    	"name": "Less 30%",
    	"property": "payload",
    	"propertyType": "msg",
    	"rules": [
        	{
            	"t": "lt",
            	"v": "30",
            	"vt": "str"
        	}
    	],
    	"checkall": "true",
    	"repair": false,
    	"outputs": 1,
    	"x": 900,
    	"y": 1060,
    	"wires": [
        	[
            	"589c720b5a2be2f2"
        	]
    	]
	},
	{
    	"id": "a067feaf7d06fabf",
    	"type": "function",
    	"z": "35700ef4.b4cc62",
    	"g": "8c22d8c9580d2858",
    	"name": "",
    	"func": "msg.payload = {}nmsg.payload.chatId = CHANGE_CHAT_IDnmsg.payload.type = "message"nmsg.payload.content = "Voltage low 3.5v! [myDacha]"nreturn msg;",
    	"outputs": 1,
    	"noerr": 0,
    	"initialize": "",
    	"finalize": "",
    	"libs": [],
    	"x": 1120,
    	"y": 920,
    	"wires": [
        	[
            	"f5b3043a.2a3ff8"
        	]
    	]
	},
	{
    	"id": "2686f0fe374cc52d",
    	"type": "function",
    	"z": "35700ef4.b4cc62",
    	"g": "8c22d8c9580d2858",
    	"name": "",
    	"func": "msg.payload = {}nmsg.payload.chatId = CHANGE_CHAT_IDnmsg.payload.type = "message"nmsg.payload.content = "Battery low 30%! [myDacha]"nreturn msg;",
    	"outputs": 1,
    	"noerr": 0,
    	"initialize": "",
    	"finalize": "",
    	"libs": [],
    	"x": 1120,
    	"y": 1000,
    	"wires": [
        	[
            	"f5b3043a.2a3ff8"
        	]
    	]
	},
	{
    	"id": "d6992fd1eb26d528",
    	"type": "trigger",
    	"z": "35700ef4.b4cc62",
    	"g": "8c22d8c9580d2858",
    	"name": "",
    	"op1": "1",
    	"op2": "0",
    	"op1type": "str",
    	"op2type": "str",
    	"duration": "5",
    	"extend": true,
    	"overrideDelay": false,
    	"units": "s",
    	"reset": "",
    	"bytopic": "all",
    	"topic": "topic",
    	"outputs": 1,
    	"x": 910,
    	"y": 960,
    	"wires": [
        	[
            	"4ecdc496c0ecd286"
        	]
    	]
	},
	{
    	"id": "589c720b5a2be2f2",
    	"type": "trigger",
    	"z": "35700ef4.b4cc62",
    	"g": "8c22d8c9580d2858",
    	"name": "",
    	"op1": "1",
    	"op2": "0",
    	"op1type": "str",
    	"op2type": "str",
    	"duration": "5",
    	"extend": true,
    	"overrideDelay": false,
    	"units": "s",
    	"reset": "",
    	"bytopic": "all",
    	"topic": "topic",
    	"outputs": 1,
    	"x": 910,
    	"y": 1000,
    	"wires": [
        	[
            	"b9d28942538208e4"
        	]
    	]
	},
	{
    	"id": "4ecdc496c0ecd286",
    	"type": "switch",
    	"z": "35700ef4.b4cc62",
    	"g": "8c22d8c9580d2858",
    	"name": "",
    	"property": "payload",
    	"propertyType": "msg",
    	"rules": [
        	{
            	"t": "eq",
            	"v": "1",
            	"vt": "str"
        	}
    	],
    	"checkall": "true",
    	"repair": false,
    	"outputs": 1,
    	"x": 1090,
    	"y": 860,
    	"wires": [
        	[
            	"a067feaf7d06fabf"
        	]
    	]
	},
	{
    	"id": "b9d28942538208e4",
    	"type": "switch",
    	"z": "35700ef4.b4cc62",
    	"g": "8c22d8c9580d2858",
    	"name": "",
    	"property": "payload",
    	"propertyType": "msg",
    	"rules": [
        	{
            	"t": "eq",
            	"v": "1",
            	"vt": "str"
        	}
    	],
    	"checkall": "true",
    	"repair": false,
    	"outputs": 1,
    	"x": 1110,
    	"y": 1060,
    	"wires": [
        	[
            	"2686f0fe374cc52d"
        	]
    	]
	},
	{
    	"id": "bd842c6db6853e79",
    	"type": "debug",
    	"z": "35700ef4.b4cc62",
    	"g": "8c22d8c9580d2858",
    	"name": "",
    	"active": true,
    	"tosidebar": true,
    	"console": false,
    	"tostatus": false,
    	"complete": "false",
    	"statusVal": "",
    	"statusType": "auto",
    	"x": 870,
    	"y": 860,
    	"wires": []
	},
	{
    	"id": "26d9630fb660bc9d",
    	"type": "ui_gauge",
    	"z": "35700ef4.b4cc62",
    	"g": "8c22d8c9580d2858",
    	"name": "",
    	"group": "189e2e48.d109d2",
    	"order": 3,
    	"width": "3",
    	"height": "2",
    	"gtype": "gage",
    	"title": "Battery",
    	"label": "units",
    	"format": "{{value}}",
    	"min": 0,
    	"max": "100",
    	"colors": [
        	"#ff0000",
        	"#e6e600",
        	"#008000"
    	],
    	"seg1": "30",
    	"seg2": "70",
    	"x": 720,
    	"y": 1060,
    	"wires": []
	},
	{
    	"id": "0d4f374253a9f8d4",
    	"type": "ui_gauge",
    	"z": "35700ef4.b4cc62",
    	"g": "8c22d8c9580d2858",
    	"name": "",
    	"group": "189e2e48.d109d2",
    	"order": 4,
    	"width": "3",
    	"height": "2",
    	"gtype": "gage",
    	"title": "Voltage",
    	"label": "units",
    	"format": "{{value}}",
    	"min": 0,
    	"max": "5",
    	"colors": [
        	"#ff0000",
        	"#e6e600",
        	"#008000"
    	],
    	"seg1": "",
    	"seg2": "",
    	"x": 620,
    	"y": 1000,
    	"wires": []
	},
	{
    	"id": "e9b9f2a1a8421187",
    	"type": "function",
    	"z": "35700ef4.b4cc62",
    	"g": "3a19bea8.e27032",
    	"name": "",
    	"func": "msg.payload = {}nmsg.payload.chatId = CHANGE_CHAT_IDnmsg.payload.type = "message"nmsg.payload.content = "Test sms! [myDacha]"nreturn msg;n",
    	"outputs": 1,
    	"noerr": 0,
    	"initialize": "",
    	"finalize": "",
    	"libs": [],
    	"x": 990,
    	"y": 400,
    	"wires": [
        	[
            	"f5b3043a.2a3ff8"
        	]
    	]
	},
	{
    	"id": "548c306e6e2c111f",
    	"type": "debug",
    	"z": "35700ef4.b4cc62",
    	"g": "3a19bea8.e27032",
    	"name": "",
    	"active": true,
    	"tosidebar": true,
    	"console": false,
    	"tostatus": false,
    	"complete": "false",
    	"statusVal": "",
    	"statusType": "auto",
    	"x": 340,
    	"y": 400,
    	"wires": []
	},
	{
    	"id": "fddd6117.b012f",
    	"type": "ui_group",
    	"name": "Camera",
    	"tab": "39cad93e.df1b16",
    	"order": 3,
    	"disp": true,
    	"width": "10",
    	"collapse": false
	},
	{
    	"id": "371d08d2.682078",
    	"type": "ui_group",
    	"name": "Проверки",
    	"tab": "39cad93e.df1b16",
    	"order": 4,
    	"disp": true,
    	"width": "6",
    	"collapse": false
	},
	{
    	"id": "189e2e48.d109d2",
    	"type": "ui_group",
    	"name": "Состояние",
    	"tab": "39cad93e.df1b16",
    	"order": 1,
    	"disp": true,
    	"width": "6",
    	"collapse": false
	},
	{
    	"id": "172f5cc2.238383",
    	"type": "ui_group",
    	"name": "Показатели",
    	"tab": "39cad93e.df1b16",
    	"order": 2,
    	"disp": true,
    	"width": "18",
    	"collapse": false
	},
	{
    	"id": "d9de0dcc.3d2a6",
    	"type": "telegram bot",
    	"botname": "house_bot",
    	"usernames": "",
    	"chatids": "",
    	"baseapiurl": "",
    	"updatemode": "polling",
    	"pollinterval": "300",
    	"usesocks": false,
    	"sockshost": "",
    	"socksport": "6667",
    	"socksusername": "anonymous",
    	"sockspassword": "",
    	"bothost": "",
    	"botpath": "",
    	"localbotport": "8443",
    	"publicbotport": "8443",
    	"privatekey": "",
    	"certificate": "",
    	"useselfsignedcertificate": false,
    	"sslterminated": false,
    	"verboselogging": false
	},
	{
    	"id": "93c1182fe25c4f4a",
    	"type": "ina-sensor-manager",
    	"address": "0x36",
    	"delay": "1000",
    	"ohms": "0.01",
    	"customResistor": false
	},
	{
    	"id": "39cad93e.df1b16",
    	"type": "ui_tab",
    	"name": "Dacha",
    	"icon": "dashboard",
    	"order": 2,
    	"disabled": false,
    	"hidden": false
	}
]

Загружаем проект

В правом верхнем углу вызываем меню и выбираем «Импорт».

https://lh7-us.googleusercontent.com/aqgnp8JXyQrtimUBYamVUdTmHErObNeowU82p0o-REbhJ3u8qUkFjZsdrYHvJyzeWg3fagK5g2XMyzowQQXotTvDdBVkZw5HWdcgtD4d1SgDy7P5ZfFPM7tL0WMNxpyCokDI7G5Kt7h6BCcD1D6C_4c

Далее можно вставить содержимое json-файла из буфера обмена или указать файл для импорта.

https://lh7-us.googleusercontent.com/lHHBxWUN1PG2JHcHSQ-RJVU1TpW0qEkiRI8pAT6NZHO8LgyqIhPJgSKVcmWqHD1IqGmO-f2p-iN-N4041CZ3iLBij4cSHu1YprV96Y2lXeJWkiLjupL5O9OSkrtpFw3zoNut1XWVi5BNGj9WSpfvBnY

После успешного импорта json-файла в основном окне отобразятся все цепочки потока:

https://lh7-us.googleusercontent.com/zmca0zKXu0EmKYoHE2Y2c4iYXKMDg8UiXKV8hWwEi675dnmuEGjvpdUbbNe66v-w0-w_BB2nJvb3ALoSFSMun6BjAPmLfigQiRc-D2AE_d74E-AGfnEv7sDd72HsiqAUdQI14BxBcf631IAz1izCLyA

Результат импорта json-файла

Проект разбит на пять блоков (сверху вниз, слева направо): 

  • постановка на охрану и обработка сигналов с IP-камеры;

  • логика работы с датчиком температуры;

  • отображение системных метрик Raspberry Pi; 

  • обработка данных с UPS Lite;

  • управление Raspberry Pi Camera.

Для удобства просмотра сделал все отдельными блоками. В случае, если функционал не используется, например, отсутствует UPS Lite, блок можно легко удалить. 

После загрузки потоки существуют только в редакторе, осталось загрузить их на сервер нажатием кнопки «Развернуть» в правом верхнем углу Node-RED.

На этом этапе система уже может выполнять охранные функции, оповещать в Telegram и предоставлять локальное онлайн-наблюдение. 

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

Настраиваем роутер и NAS

Для себя решил, что хранить видео с IP-камеры буду удаленно на своем NAS. Это позволит собирать записи в высоком качестве за долгий период, а также обеспечит быстрый просмотр нужного фрагмента видео (все-таки домашний интернет значительно быстрее дачного). 

Так как захват видео с IP-камеры инициализируется самим NAS, а LTE-модем не имеет белого IP-адреса, нам потребуется VPN. В качестве VPN-сервера я выбрал Wireguard. Его преимуществом является быстрая работа (что важно для мобильного интернета) и простая настройка. Мой роутер от Mikrotik умеет работать с Wireguard. 

Настройка роутера сводится к добавлению интерфейса Wireguard, генерации приватного и публичного ключей сервера, плюс добавлении клиентов и маршрутов. Настройка Wireguard на роутерах Mikrotik вариант Site to Site подробно описана на официальном сайте [14]. Вариант настройки через графический интерфейс можно найти на Хабре, например тут [15]. Мой вариант настройки, где локальная сеть имеет адрес 192.168.0.255, VPN адрес 10.10.10.1, а локальная сеть на даче - 192.168.1.255 и VPN адрес 10.10.10.2 имеет такие настройки:

/interface wireguard
add comment="WG Server" listen-port=51820 mtu=1420 name=wg_server



/interface wireguard peers
add allowed-address=10.10.10.2/32,192.168.1.0/24 comment="WG Client" interface=wg_server persistent-keepalive=25s public-key=
	"публичный ключ из файла wg-public.key"

/ip route
add disabled=no distance=2 dst-address=192.168.1.0/24 gateway=10.10.10.2 pref-src="" scope=30 target-scope=10

Для записи и хранения видео с IP-камеры NAS имеет программный модуль Surveillance Station. Это приложение, которое позволяет управлять IP-камерами из перечня поддерживаемых. Если вашей нет в списке, это не проблема, все IP-камеры имеют протокол ONVIF, который также поддерживает приложение.

Конфигурируем WireGuard на Paspberry Pi

Устанавливаем Wireguard:

$ sudo apt install wireguard

Генерируем приватный и публичный ключи клиента:

$ (umask 077 && wg genkey > wg-private.key)
$ wg pubkey < wg-private.key > wg-public.key

Создаем файл /etc/wireguard/wg0.conf с содержимым:

[Interface]
Address = 10.10.10.2/24
PrivateKey = Приватный ключ из файла wg-private.key
PostUp = /etc/wireguard/scripts/add-nat-routing.sh
PostDown = /etc/wireguard/scripts/delete-nat-routing.sh

[Peer]
PublicKey =
AllowedIPs = 10.10.10.0/24, 192.168.1.0/24б 192.168.0.0/24
Endpoint = белый IP роутера:51820

Содержимое файла wg-public.key надо вставить в настройки клиента в процессе настройки Wireguard на роутере. 

В параметры PostUp и PostDown указал путь к скриптам, которые проставляют маршруты и инициализируют LTE-модем при запуске VPN соединения, а при отключении VPN -  удаляют маршруты.

Скрипт add-nat-routing.sh

#!/bin/bash
IPT="/sbin/iptables"

IN_FACE="br0"               	# NIC connected to the internet
WG_FACE="wg0"                	# WG NIC
SUB_NET="10.10.10.0/24"        	# WG IPv4 sub/net aka CIDR
WAN_FACE="wan"
curl -H "Referer: http://192.168.99.1/index.html" "http://192.168.99.1/goform/goform_set_cmd_process?goformId=SET_CONNECTION_MODE&ConnectionMode=auto_dial"
## IPv4 ##
$IPT -t nat -A POSTROUTING -o $WG_FACE -j MASQUERADE
$IPT -t nat -A POSTROUTING -o $WAN_FACE -j MASQUERADE
$IPT -t nat -A POSTROUTING -s $SUB_NET -o $IN_FACE -j MASQUERADE

Скрипт delete-nat-routing.sh

#!/bin/bash
IPT="/sbin/iptables"
## IPv4 ##
$IPT -t nat -F

Настройки и названия интерфейсов будут описаны в разделе о  конфигурировании точки доступа. Команда curl в скрипте add-nat-routing.sh выполняет инициализацию LTE-модема, команду взял из документации к моему LTE-модему.

Конфигурируем точку доступа

Так как у Raspberry Pi Zero W есть wi-fi модуль, то я решил этим воспользоваться и сделать полноценную точку доступа для раздачи интернета на даче. Процесс создания точки доступа довольно сложный, руководствовался несколькими статьями тут [16] и тут [17]. Первым делом устанавливаем пакеты. 

Для настройки точки доступа:

sudo apt-get install hostapd

Для создания моста между wi-fi и ethernet  интерфейсами:

sudo apt-get install bridge-utils

Описал интерфейсы.

Интерфейс для локальной сети lan описал в файле /etc/network/interfaces.d/lan:

iface lan inet manual

Интерфейс для беспроводной сети wifi описал в файле /etc/network/interfaces.d/wifi:

iface wifi inet manual

Интерфейс LTE-модема wan описал в файле /etc/network/interfaces.d/wan:

iface wan inet static
    	address 192.168.99.100
    	netmask 255.255.255.0
    	gateway 192.168.99.1

Создаем мост и включаем в него интерфейсы lan и wifi:

sudo brctl addbr br0
sudo brctl addif br0 lan
sudo brctl addif br0 wifi

 Мост br0 описал в файле /etc/network/interfaces.d/br0:

auto br0
iface br0 inet static
	bridge_ports wifi lan
	address 192.168.1.1
	broadcast 192.168.1.255
	netmask 255.255.255.0

Чтобы мост заработал, исключаем из DHCP интерфейс wan, добавив в конец файла  /etc/dhcpcd.conf:

denyinterfaces wan

Добавляем диапазон для раздачи адресов в файл /etc/dhcp/dhcpd.conf:

subnet 192.168.1.0 netmask 255.255.255.0 {
    	range 192.168.1.100 192.168.1.254;
    	option routers 192.168.1.1;
    	option subnet-mask 255.255.255.0;
}

Осталось настроить саму точку доступа. Для этого создадим файл /etc/hostapd/hostapd.conf со следующими параметрами:

## Wireless network interface ###
interface=wifi
### Driver ###
driver=nl80211
### Network name SSID ###
ssid=НАЗВАНИЕ_WIFI_СЕТИ
### Set frequency to 2.4 Ghz ###
hw_mode=g
### Channel number ###
channel=4
### Enable Wi-Fi N ###
ieee80211n=1
### Enable WMM ###
wmm_enabled=1
### Enable 40 Mhz channels ###
ht_capab=[HT40][SHORT-GI-20][DSSS_CCK-40]
### Allow all MAC Address ###
macaddr_acl=0
### Use WPA Auth ###
auth_algs=1
### Require clients to know the network name ###
ignore_broadcast_ssid=0
### Use WPA2 ###
wpa=2
### Enable Pre-Shared Key ###
wpa_key_mgmt=WPA-PSK
### Network key ###
wpa_passphrase=ПАРОЛЬ_СЕТИ_WIFI
### Use AES ###
rsn_pairwise=CCMP
bridge=br0

Обратите внимание, что там где указано НАЗВАНИЕ_СЕТИ_WIFI и ПАРОЛЬ_СЕТИ_WIFI следует придумать свои.

Указываем расположение конфигурационного файла для точки доступа в файле /etc/default/hostapd:

DAEMON_CONF="/etc/hostapd/hostapd.conf"

Осталось включить переадресацию трафика, раскомментировав в файле /etc/sysctl.conf строку:

net.ipv4.ip_forward=1

Выполняем рестарт Raspberry. После рестарта мы получим полноценный роутер с раздачей IP-адресов как по wi-fi, так и по ethernet, плюс подключенный интернет через LTE-модем и настроенный VPN-клиент.

Проверяем доступ в интернет. Проверяем сетевую связность между домашним роутером и Raspberry по локальным адресам.

В итоге получилось Site-to-Site соединение между домашней и дачной сетями. Это позволяет осуществлять доступ ко всем устройствам и сервисам на даче по внутренним адресам из домашней локальной сети через VPN, и доступ в интернет и в домашнюю сеть из локальной сети на даче.

Что дальше

В таком виде система работает уже более трех лет, сейчас появилась потребность добавить еще одну IP-камеру. Для этого достаточно подключить USB Ethernet-адаптер или добавить еще один Raspberry Pi Ethernet/USB HUB HAT к Raspberry Pi. Входов-выходов у Raspberry достаточно, чтобы добавить датчик дыма, а датчик температуры использовать только для наблюдения. Есть возможность добавить релейный модуль для включения света на участке при срабатывании движения от IP-камеры. Главное, что модернизация не потребует замены распределительного щитка и легко уместится в существующем.

Альтернатива

Raspberry Pi Zero был выбран исключительно из-за его размеров. Если размеры распределительного щитка позволяют, то проще будет использовать Raspberry Pi 3 и новее. В нем уже есть Ethernet и USB-порты. 

Конечно, можно обойтись и без I/O в IP-камере и использовать камеру с wi-fi, а управление и оповещение осуществлять из приложения через облако вендора. Для себя я исключил этот вариант только потому, что считаю проводное соединение надежнее, а облака в последнее время все чаще становятся платными.

Что еще? Недавно на рынок вышел интересный продукт - Mikrotik KNOT. Это полноценный роутер с wi-fi и POE, но самое главное - у него есть GPIO, что позволит подключить не только вход-выход IP-камеры, но и датчики, в том числе аналоговые.

*Статья написана в рамках ХабраЧелленджа 0.1, который прошел в ЛАНИТ осенью 2023 года

Автор:
Vutshi

Источник [18]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/diy/388456

Ссылки в тексте:

[1] UPS Lite v1.2 Power HAT: https://aliexpress.ru/item/1005001733452340.html?spm=a2g2w.detail.rcmdprod.0.5599b413VvZlEZ&mixer_rcmd_bucket_id=aerabtestalgoRecommendAbV4_testPdpMoreCandSources&ru_algo_pv_id=c18dd8-476f2e-ce797c-0fa459&scenario=pcDetailBottomMoreOtherSeller&sku_id=12000017383301011&traffic_source=recommendation&type_rcmd=core

[2] антивандальная кнопка с подсветкой и фиксацией: https://aliexpress.ru/item/32956631402.html?spm=a2g2w.orderdetail.0.0.5ee74aa6jl1KYd&sku_id=12000020988158385

[3] модуль датчика температуры DS18B20: https://arduino-kit.ru/product/modul-datchika-temperaturyi-ds18b20

[4] POE-адаптер 220v: https://www.ozon.ru/product/poe-inzhektor-esvi-poe-11-standart-poe-ieee-802-3af-s-byudzhetom-26w-521557533/?asb=F8srikrZT9qcynhLBvc5SYEsOv8Cp7ttJA6POdlFe98%253D&asb2=pvVZvGv-TFyCuUhOdf0DgZgV-eedKGvmOV2K9BG1S_1C9UPTulawUgES0l9GTwy4&avtc=1&avte=2&avts=1696443690&keywords=poe+220

[5] node-red-contrib-boolean-logic: https://flows.nodered.org/node/node-red-contrib-boolean-logic

[6] node-red-contrib-camerapi: https://flows.nodered.org/node/node-red-contrib-camerapi

[7] node-red-contrib-cpu: https://flows.nodered.org/node/node-red-contrib-cpu

[8] node-red-contrib-easybotics-ina219-sensor: https://flows.nodered.org/node/node-red-contrib-easybotics-ina219-sensor

[9] node-red-contrib-edge-trigger: https://flows.nodered.org/node/node-red-contrib-edge-trigger

[10] node-red-contrib-sensor-ds18b20: https://flows.nodered.org/node/node-red-contrib-sensor-ds18b20

[11] node-red-contrib-telegrambot: https://flows.nodered.org/node/node-red-contrib-telegrambot

[12] node-red-node-pi-gpio: https://flows.nodered.org/node/node-red-node-pi-gpio

[13] сайте: https://www.raspberrypi.com/documentation/computers/getting-started.html

[14] сайте: https://help.mikrotik.com/docs/display/ROS/WireGuard

[15] тут: https://habr.com/ru/articles/702164/

[16] тут: https://thepi.io/how-to-use-your-raspberry-pi-as-a-wireless-access-point/

[17] тут: https://learn.sparkfun.com/tutorials/setting-up-a-raspberry-pi-3-as-an-access-point/all

[18] Источник: https://habr.com/ru/companies/lanit/articles/774410/?utm_source=habrahabr&utm_medium=rss&utm_campaign=774410