Собираем умный дом на Raspberry Pi собственноручно. Онлайн конструктор веб-интерфейса для управления Raspberry Pi Умный дом python

Контроллеры заготовлены, датчики развешаны, провода затянуты. Дело за малым - выбрать «мозг» умного дома. Устройство, которое сможет решать ряд задач по управлению всеми умными функциями. Конечно, есть Arduino Tian или . Но есть и монстры микрокомпьютерного мира вроде чрезвычайно популярного Raspberry Pi, который способен на все, что требуется от современного интеллектуального жилища.

В чем отличия Raspberry Pi от Arduino

Прежде всего, стоит оговориться, что Arduino - это не компьютер, а Raspberry Pi - не микроконтроллер. С этими двумя терминами постоянно возникает путаница.

Это плата с электрической «обвязкой» и контроллером ATmega. Она позволяет работать с цифровыми и аналоговыми входами/выходами на очень низком уровне, не имеет операционной системы как таковой и выполняет загруженный в нее код непрерывно. Arduino потребляет смешное количество электроэнергии и может месяцами работать от блока пальчиковых батареек при условии отсутствия значительной нагрузки. Но реальной вычислительной мощности у Arduino крайне мало, а реализовывать многие функции и протоколы приходится с нуля.

Raspberry Pi - это уже целый компьютер на одной плате, имеющий на борту вместо микроконтроллера SoC (Систему на кристалле) на базе архитектуры ARM, прямо как в современных мобильных устройствах. «Малина» может работать под управлением различных дистрибутивов ОС Linux или Windows 10 IoT. Она имеет множество распаянных пинов GPIO (ввода/вывода), обращаться к которым можно при помощи готовых библиотек. И, в отличие от Arduino, программы здесь запускаются внутри ОС и работают с пинами уже через программную прослойку. В большинстве случаев это абсолютно не мешает создавать простые схемы, но иногда может оказаться критичным. Важно отметить, что у Raspberry Pi нет аналоговых пинов, зато цифровых - хоть отбавляй.

Новая облачная «Малина»
Старая «Малина»

Что умеет Raspberry Pi

Существует несколько версий компьютера: первого, второго и третьего поколения, а также мини-версия Zero. К нам на обзор попала старшая модель Raspberry Pi 3 Model B в составе набора «Малина» от наших друзей из «Амперки».

Технические характеристики модели третьего поколения:

  • 4-ядерный 64-битный процессор Broadcom BCM2837 1,2 ГГц;
  • 1ГБ ОЗУ;
  • Wi-Fi и BT LE модуль BCM43438;
  • 4 USB порта стандарта 2.0.

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

Набор «Малина» от «Амперки»

Для успешной работы Raspberry Pi потребуются несколько комплектующих:

  • сама плата;
  • блок питания 5В micro-USB;
  • HDMI-кабель для подключения монитора;
  • microSD карта памяти, на которую будет установлена ОС.

Наборы от « » уже содержат все необходимое, и не только. Более старые имели индексы Y, Z в зависимости от комплектации, а в коробке можно было найти макетку, пучок кабелей, кнопки, светодиоды, резисторы и т. д. Новый набор « », который и оказался у нас на обзоре, вместо электронных компонентов включает готовую плату с множеством распаянных элементов. В отличие от предшественника, уклон здесь сделан не на работу с элементарными деталями, а в сторону интернета вещей с множеством интересных примеров.

В комплекте имеется красочное 88-страничное руководство, дающее базовое представление о самом компьютере, штатной ОС Raspbian, основах работы с командной строкой и файловой системой Linux, а также написании кода на Python. Python - относительно простой объектно-ориентированный язык программирования, который снискал немалую популярность на платформе Raspberry Pi. Руководство из набора научит основам синтаксиса и работе в IDE Thonny.

Плата с расширением для экспериментов

« » - это прокачанная версия набора « ». Если в «Матрешке» с Arduino было много очень простых экспериментов с базовыми электрическими элементами вроде конденсаторов и светодиодов, то в «Малине» главенство отдано интеграции с интернет-сервисами. Предлагаемые «Амперкой» эксперименты помогут поднять свой веб-сервер и написать небольшой сайт для управления пинами платы. Комплектная текстолитовая плата в форме облачка с кнопками и светодиодами в этом обязательно поможет.

А дальше начинается самое интересное. Вторая половина книжки подскажет, как создать бота в «ВКонтакте» и подключить его к Raspberry Pi, чтобы плате можно было отдавать команды прямо из соц. сети. А затем предлагается настроить на Raspberry торрент-качалку и написать к ней веб-интерфейс для управления, например, с телефона, подключенного к домашнему Wi-Fi.

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

Raspberry Pi для умного дома

Умельцы уже приспособили Raspberry Pi для множества задач помимо использования в качестве рабочей машины. Из него можно сделать Wi-Fi роутер или даже собственную цифровую АТС. Правда, и в том, и в другом он будет уступать специализированным аналогам, но сильная сторона «Малины» - в возможности создавать что-то свое. И нам интересно прежде всего, как его можно использовать для создания умного дома.

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

Как обычно, руководства у «Амперки» на высоте

В первом случае вся работа ложится на собственные пины Raspberry Pi. Отсутствие аналоговых пинов компенсируется наличием недорогих цифровых датчиков вроде ds18b20 или использованием специальных ADC плат расширения. Среди стандартных пинов (см. схему ниже) можно найти контакты с ШИМ, I²C, SPI и UART. Таким образом, к плате можно подключить большинство современных датчиков, а также управлять цепями через силовые ключи или блоки реле. Создавать решения для умного дома на Raspberry Pi довольно просто, тем более что в плату уже встроено множество полезных инструментов, например WiFi модуль.

Работать с сетью Интернет на Arduino куда сложнее, а вычислительные возможности микроконтроллера и вовсе скудны. Но вот парадокс: в большинстве простых задач, вроде управления светом или обогревом, применение Raspberry Pi зачастую сродни забиванию гвоздей если не микроскопом, то уж вольтметром точно. Плата Arduino потребляет меньше электроэнергии, код в ней выполняется постоянно на самом низком уровне с прямым доступом к пинам, да и стоит она в несколько раз дешевле. А сообщество энтузиастов и разработчиков ничуть не меньше.

Другое дело - сложные решения. Например, если у вас большой дом, где требуется развесить сразу ворох датчиков, а управлять хочется с планшета (или планшетов) и обогревом, и освещением, и теплыми полами, и воротами в гараже, да еще и чтобы задвижки на трубах с водой в подвале перекрывались сами, когда вы уезжаете всей семьей на выходные. Ну мало ли что. Здесь уже речь пойдет о нескольких платах, которые требуется объединить в сеть с мозговым центром. Вот тут-то Raspberry Pi и покажет себя во всей красе. Микрокомпьютер может выступать в качестве головного устройства, аккумулируя информацию с различных датчиков по всему дому и управляя всеми устройствами. Собирать и отображать информацию можно при помощи веб-сервера, а данные - хранить на SD-карте.

В таком проекте возникает вопрос синхронизации устройств. И здесь может пригодиться протокол RS-485, позволяющий по двум проводам (в идеале, с «землей») передавать сигнал на длинные расстояния, или обычная локальная сеть, благо Arduino умеет работать с простыми http-запросами, чего вполне достаточно для реализации домашней сети умного дома.

Заключение

Не стоит забывать, что Raspberry Pi - устройство для энтузиастов. И именно в этом его сильная сторона. Задавшись целью разобраться, с помощью этого микрокомпьютера можно собрать множество интересных решений, и не только для умного дома. А встроенные сетевой интерфейс и WiFi модуль позволяют легко подключить плату к локальной сети или Интернету. Удобно, что наборы вроде « » от «Амперки» уже включают все необходимое, в том числе грамотное руководство, позволяющее легко познакомиться с основами работы с Raspberry Pi.

В сложных схемах на помощь могут прийти платы-аналоги Arduino, например семейства ESP: 8266 или 32. Компактные, быстрые, со встроенным WiFi. О них мы обязательно расскажем в одном из следующих материалов.

А вы уже начали делать свой умный дом? Или только собираетесь? Поделитесь своими проектами в комментариях.

Если вы нашли ошибку, пожалуйста, выделите фрагмент текста и нажмите Ctrl+Enter .

Рисунок 1. Блочная диаграмма аппаратной части системы

Описание процесса монтажа аппаратной части системы занимает много времени, но является достаточно простым. В первую очередь следует соединить блок питания с стенной розеткой с помощью удлинителя, отрезав розетку это удлинителя. Зачистите провода и закрепите их с помощью винтов в терминалах блока питания. Далее соедините Raspberry Pi с блоком питания, отрезав разъем типа A от кабеля USB и соединив провода с соответствующими выводами блока питания, и вставьте разъем micro USB в разъем питания RPi. После этого следует зачистить оба конца двух жил гибкого кабеля и соединить их с соответствующими терминалами с обозначениями GND и JDVcc блока питания и блока реле. Наконец, следует удалить джампер, соединяющий вывод с обозначением JDVcc с выводом с обозначением Vcc. В том случае, если вы не удалите этот дампер, на предназначенные для напряжения 3.3 В выводы RPi будет подано напряжение в 5 В, которое с высокой вероятностью выведет компьютер из строя.

Теперь, когда питание подведено ко всем терминалам, следует соединить линии IN1-IN8 модуля реле с соответствующими выводами разъема GPIO с помощью гибкого кабеля таким образом, как показано на Рисунке 2. Представленный в данной статье код был разработан для случая, когда выводы IN1-IN7 соединены с выводами GPIO1-GPIO7. В том случае, если вы решите соединить данные выводы по-другому, вам придется модифицировать соответствующим образом ваш код.

Схема расположения выводов разъема GPIO Raspberry Pi приведена на Рисунке 2. На порты ввода-вывода Raspberry Pi подается напряжение 3.3 В, а модуль реле работает с напряжением 5 В. Однако, реле изолированы от выводов GPIO Raspberry Pi при помощи оптопар. На оптопары может подаваться напряжение 3.3 В с вывода Vcc. На вывод Vcc модуля реле может быть подано напряжение 3.3 В с разъема GPIO Raspberry Pi. Убедитесь в том, что вы убрали джампер, замыкающий выводы Vcc и JDVcc модуля реле. На вывод JDVcc должно подаваться напряжение 5 В для корректной работы реле. Рассматриваемый модуль реле размыкает контакты в активном состоянии. Из этого следует, что вы должны заземлить терминалы IN1-IN8 для включения реле.

Рисунок 2. Схема расположения выводов разъема GPIO Raspberry Pi

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

Обрежьте остатки кабелей удлинителей с вилками и закрепите провода в соответствующих терминалах модуля реле. Также подключите провода кабеля, который впоследствии будет связывать систему со стенной розеткой, к соответствующим терминалам модуля реле. Вся аппаратная часть системы может быть размещена в пенале или аналогичном контейнере. Подумайте о корпусе заранее, чтобы по окончании работы над аппаратной частью системы избежать необходимости в отсоединении и повторном присоединении проводов к терминалам модуля реле. Кроме того, я вставил несколько закрепляемых с помощью винтов зажимов для кабелей в соответствующие отверстия корпуса для ограничения натяжения кабелей (Рисунок 3).

Рисунок 3. Монтаж аппаратной части системы

Программное окружение

Я начал создание своего программного окружения с установки образа операционной системы Raspbian. Перед началом установки образа операционной системы вам потребуется подготовить дисплей, поддерживающий передачу изображения по HDMI, клавиатуру и мышь с разъемами USB, а также сетевой кабель для соединения с системой по протоколу Ethernet. Также вы можете установить соединение с системой посредством адаптера Wi-Fi. Создайте загрузочную SD-карту для первой загрузки системы в соответствии с инструкциями, приведенными на ресурсе http://www.raspberrypi.org/documentation/installation/installing-image . В процессе первой загрузки системы установщик осуществит настройку операционной системы и разместит данные из ее образа на всем доступном пространстве карты памяти. После первой загрузки вы должны иметь возможность входа в систему с помощью стандартных данных учетной записи пользователя (имя пользователя "pi" и пароль "raspberry").

Обновление системы является разумным действием, которое должно выполняться сразу же после успешного входа в систему. Образ операционной системы Raspbian базируется на пакетах программного обеспечения дистрибутива Debian и использует приложение aptitude в качестве менеджера пакетов программного обеспечения. Кроме того, вам понадобятся пакеты программного обеспечения с именами python , pip и git . Я также мог бы порекомендовать установку Webmin для упрощения процесса администрирования системы. Инструкции по установке Webmin приведены на ресурсе http://www.webmin.com/deb.html (следуйте рекомендациям, приведенным в разделе "Using the Webmin APT repository"):

Sudo apt-get update && sudo apt-get dist-upgrade sudo apt-get install python python-pip git git-core

После этого вам придется настроить соединение с использованием адаптера Wi-Fi. Вы можете найти подробные инструкции на ресурсе http://www.raspberrypi.org/documentation/configuration/wireless . Я рекомендую использовать вариант wicd-curses . На данном этапе вы можете изменить параметры настройки Raspberry Pi с помощью команды sudo raspi-config . После ввода данной команды вы получите доступ к удобному графическому интерфейсу, который позволит вам установить значения таких параметров, как объем оперативной памяти, разделяемой с графическим процессором, параметры быстродействия центрального процессора, режим использования графического интерфейса в процессе загрузки и других.

Другим полезным инструментом является интегрированная среда разработки Cloud 9 IDE . Cloud 9 IDE позволит вам редактировать свой код на уровне Raspberry Pi посредством веб-браузера. Данная интегрированная среда разработки также предоставит вам доступ к интерфейсу командной строки в рамках веб-браузера. Вы можете разрабатывать и исполнять любой код, не покидая свой веб-браузер. Интегрированная среда разработки Colud 9 IDE требует наличия определенной версии фреймворка NodeJS. Использование неподдерживаемой версии фреймворка повлечет за собой постоянные аварийные завершения работы сервера Cloud 9, которые могут привести любого пользователя в уныние. Инструкции по установке фреймворка NodeJS на компьютер Raspberry Pi приведены на ресурсе http://weworkweplay.com/play/raspberry-pi-nodejs .

Программное обеспечение

Я решил создавать пользовательский интерфейс своей системы с использованием технологий HTML5, CSS3 и JavaScript. Комбинация трех упомянутых технологий является мощным инструментом для создания пользовательских интерфейсов. Язык программирования JavaScript позволяет использовать простой API для взаимодействия с серверами. Кроме того, существует множество библиотек для языка программирования JavaScript, таких, как JQuery, Bootstrap и других, из которых можно выбрать наиболее подходящую. HTML5 предоставляет API WebSocket, позволяющее веб-браузеру поддерживать соединение в рабочем состоянии и осуществлять обмен данными посредством этого соединения. Это обстоятельство делает API WebSocket особенно полезным для реализации динамических приложений и приложений для потоковой передачи данных, таких, как игры и чаты. Каскадные таблицы стилей CSS полезны для стилизации различных элементов страницы HTML. В случае корректного использования они позволяют создавать динамические пользовательские интерфейсы путем изменения стилей элементов страниц при наступлении тех или иных событий. Для данного проекта я выбрал фреймворк JQuery для обработки событий, Bootstrap CSS для размещения кнопок в форме сетки и язык программирования JavaScript для реализации механизмов обмена данными на основе API WebSocket.

Библиотеки

Серверное приложение, работающее на уровне Raspberry Pi, должно управлять состоянием выводов разъема GPIO платы Raspberry Pi. Оно также должно предоставлять интерфейс HTTP для передачи данных графического интерфейса и интерфейс WebSocket для передачи сообщений с командами и данными состояния. Готового к установке серверного приложения с такими специфическими функциями попросту не существует, поэтому я принял решение о создании своей собственной реализации сервера с использованием языка программирования Python. Для упрощения разработки описанного серверного приложения с использованием языка программирования Python доступны модули с реализациями методов для работы с интерфейсом GPIO Raspberry Pi, для создания сервера HTTP и для работы с интерфейсом WebSockets. Так как все перечисленные модули предназначены для выполнения поставленных задач, мне пришлось разработать минимальный объем кода.

Однако, упомянутые модули не включены в комплект поставки интерпретатора Python и должны устанавливаться отдельно. В первую очередь вам понадобится модуль для управления состоянием выводов разъема GPIO Raspberry Pi. Простейший способ изменения состояния выводов данного разъема заключается в использовании библиотеки RPi.GPIO, доступной по адресу https://pypi.python.org/pypi/RPi.GPIO . Вы можете установить соответствующий модуль с помощью следующей команды:

Sudo pip install RPi.GPIO

Работа с модулем RPi.GPIO не связана с какими-либо сложностями. Вы можете найти примеры использования данного модуля по адресу . На первом шаге работы с модулем необходимо осуществить импорт его кода в код проекта. После этого вам придется выбрать режим работы. В качестве идентификатора режима работы может использоваться либо константа GPIO.BOARD, либо константа GPIO.BCM. Выбор режима работы обуславливает использование чипа BCM или выводов разъема ввода-вывода при ссылках на номера выводов во всех последующих командах. Далее следует указать, используются ли выводы из рассматриваемого разъема для ввода или вывода. Теперь вы можете использовать выводы данного разъема по назначению. Наконец, вам придется осуществить вызов метода cleanup() для сброса состояния выводов разъема GPIO. В Листинге 1 показан простейший пример использования модуля RPi.GPIO.

Листинг 1. Использование модуля RPi.GPIO

Import RPi.GPIO as GPIO # импортирование кода модуля в код проекта GPIO.setmode(GPIO.BOARD) # указание на то, что нумерация используется для обозначения выводов разъема GPIO.setup(0, GPIO.IN) # указание на то, что канал 0 будет использоваться для ввода GPIO.setup(1, GPIO.OUT) # указание на то, что канал 1 будет использоваться для вывода var1=GPIO.input(0) # чтение состояния канала 0 GPIO.output(1, GPIO.HIGH) # установка логической единицы на канале 1 GPIO.cleanup() # сброс состояния выводов разъема GPIO.

Введение

Данный проект берет свое начало в 2014 году, когда передо мной встала задача обеспечить удаленное управления обогревательными приборами в своем загородном доме. Дело в том, что практически каждые выходные мы с семьей проводим на даче. И если летом мы, задержавшись по тем или иным причинам в городе, приехав в дом могли сразу лечь спать, то зимой, когда температура опускается до -30 градусов, мне приходилось тратить по 3-4 часа на протопку дома. Я видел следующие пути решения данной проблемы:

    "Неумное решение" - можно оставлять включенными обогреватели со встроенными термостатами на минимальной температуре поддержания тепла. Собственно ничего "умного" в этом решении нет, но 24/7 работающие обогревательные приборы в деревянном загородном доме не внушают доверия. Хотелось хотя бы минимального контроля над их состоянием, автоматизации и какой-нибудь обратной связи;

    GSM-розетки - данным решением пользуются мои соседи по дачному участку. Если кто-то не знаком с ними, то это просто управляемый посредством SMS команд переходник, который включается в розетку, а сам обогреватель включается в него. Не самое бюджетное решение, если нужно обеспечить обогрев целого дома - ссылка на маркет . Я вижу его как самое простое и менее трудозатратное в реализации, но имеющее минусы в процессе эксплуатации, такие как: целый ворох сим карт и работы по поддержанию их положительного баланса, так как для каждой комнаты нужен минимум один обогреватель, ограниченность и неудобства их контроля по средствам SMS;

  1. "Умный дом" - собственно решения, построенные на реализации "умного дома".

Как наиболее перспективное решение мною был выбран третий вариант и следующим вопросом на повестке дня стал - "Какую платформу для реализации выбрать?".


Уже не помню сколько я потратил время на поиски подходящих вариантов, но в итоге из бюджетных и доступных в магазинах решений я нашел системы: NooLite и CoCo (сейчас уже переименовали в Trust). При их сравнении решающую роль для меня сыграло то, что у NooLite есть открытое и задокументированное API для управления любыми его блоками. На тот момент необходимости в нем не было, но я сразу отметил, какую гибкость в дальнейшем это может дать. Да и цена у NooLite была существенно ниже. В итоге я остановил свой выбор именно на NooLite.

Реализация 1 - автоматизация NooLite

Система NooLite состоит из силовых модулей (под разные типы нагрузок), датчиков (температура, влажность, движение) и управляющего ими оборудования: радио пульты, настенные выключателей, USB-адаптеров для компьютера или Ethernet-шлюза PR1132. Все это можно использовать в различных комбинациях, соединять их между собой напрямую или управлять через usb-адаптеры или шлюз, подробнее об этом можете почитать на официальном сайте производителя.


Для моей задачи центральным элементом умного дома я выбрал Ethernet-шлюза PR1132, который будет управлять силовыми блоками и получать информацию с датчиков. Для работы Ethernet-шлюза необходимо подключить его к сети кабелем, поддержки Wi-Fi в нем нет. На тот момент у меня в доме уже была организована сеть, состоящая из WiFi-маршрутизатора Asus rt-n16 и USB--модема для доступа к интернету. Поэтому весь монтаж NooLite для меня заключался лишь в том, чтобы подключить шлюз кабелем к маршрутизатору, расположить в доме радиодатчики температуры и смонтировать силовые блоки в центральном электрощитке.


У NooLite есть ряд силовых блоков для разной подключаемой нагрузки. Самый "мощный" блок может управлять нагрузкой до 5000 Вт. Если требуется управление большей нагрузкой, как в моем случае, то можно сделать подключение нагрузки через управляемое реле, которым, в свою очередь, будет управлять силовой блок NooLite.




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



Ethernet-шлюза PR1132 и маршрутизатор Asus rt-n16



Беспроводной датчик температуры и влажности PT111



Электрощиток и силовой блок для наружного монтажа SR211 - в дальнейшем вместо этого блока я использовал блок для внутреннего монтажа и поместил его прямо в электрощитке


Ethernet-шлюз PR1132 имеет web-интерфейс через которой осуществляется привязка/отвязка силовых блоков, датчиков и управление ими. Сам интерфейс выполнен в довольно "топорном" минималистическом стиле, но этого вполне достаточно для доступа ко всему необходимому функционалу системы:




Настройки




Управление




Страница одной группы выключателей


Подробно о привязке и настройке всего этого - опять же на официальном сайте.


На тот момент я мог:

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

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

Реализация 2 - удаленный доступ к умному дому

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


Как я упомянул в предыдущем разделе - дачная сеть имеет доступ к интернету через usb модем одного из мобильных операторов. По умолчанию мобильные модемы имеют серый ip адрес и без дополнительных ежемесячных трат белого фиксированного ip не получить. При таком сером IP не помогут и различные no-ip сервисы.


Единственный вариант, который мне удалось на тот момент придумать - VPN. На городском маршрутизаторе у меня был настроен VPN-сервер, которым я время от времени пользовался. Мне было необходимо настроить на дачном роутере VPN-клиент и прописать статические маршруты до дачной сети.




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


В результате дачный роутер постоянно держал VPN соединение с городским роутером и для доступа к шлюзу NooLite мне нужно было с клиентского устройства (ноутбук, телефон) подключится по VPN к городскому маршрутизатору.


На этом этапе я мог:

  • получить доступ к умному дому из любого места;

В целом это практически на 100% покрывало первоначальную задачу. Однако я понимал, что данная реализация далека от оптимальной и удобной в использовании, так как каждый раз я должен был выполнять ряд дополнительных действий по подключению к VPN. Для меня это не было особой проблемой, однако для остальных членов семьи это было не очень удобно. Так же в этой реализации было очень много посредников, что сказывалось на отказоустойчивости всей системы в целом. Однако на некоторое время я остановился именно на этом варианте.

Реализация 3 - Telegram bot

С появлением ботов в Telegram я взял на заметку, что это смогло бы стать довольно удобным интерфейсом для управления умным домом и, как только у меня появилось достаточно свободного времени, я приступил к разработке на Python 3.


Бот должен был где-то находится и, как самое энергоэффективное решение, я выбрал Raspberry Pi. Хоть это и был мой первый опыт работы с ним, особых сложностей в его настройке не возникло. Образ на карту памяти, ethernet кабель в порт и по ssh - полноценный Linux.


Как я уже говорил - у NooLite есть задокументированное API, которое и пригодилось мне на данном этапе. Для начала я написал простенькую обертку для более удобного взаимодействия с API:


noolite_api.py

""" NooLite API wrapper """ import requests from requests.auth import HTTPBasicAuth from requests.exceptions import ConnectTimeout, ConnectionError import xml.etree.ElementTree as ET class NooLiteSens: """Класс хранения и обработки информации, полученной с датчиков Пока как таковой обработки нет """ def __init__(self, temperature, humidity, state): self.temperature = float(temperature.replace(",", ".")) if temperature != "-" else None self.humidity = int(humidity) if humidity != "-" else None self.state = state class NooLiteApi: """Базовый враппер для общения с NooLite""" def __init__(self, login, password, base_api_url, request_timeout=10): self.login = login self.password = password self.base_api_url = base_api_url self.request_timeout = request_timeout def get_sens_data(self): """Получение и прасинг xml данных с датчиков:return: список NooLiteSens объектов для каждого датчика:rtype: list """ response = self._send_request("{}/sens.xml".format(self.base_api_url)) sens_states = { 0: "Датчик привязан, ожидается обновление информации", 1: "Датчик не привязан", 2: "Нет сигнала с датчика", 3: "Необходимо заменить элемент питания в датчике" } response_xml_root = ET.fromstring(response.text) sens_list = for sens_number in range(4): sens_list.append(NooLiteSens(response_xml_root.find("snst{}".format(sens_number)).text, response_xml_root.find("snsh{}".format(sens_number)).text, sens_states.get(int(response_xml_root.find("snt{}".format(sens_number)).text)))) return sens_list def send_command_to_channel(self, data): """Отправка запроса к NooLite Отправляем запрос к NooLite с url параметрами из data:param data: url параметры:type data: dict:return: response """ return self._send_request("{}/api.htm".format(self.base_api_url), params=data) def _send_request(self, url, **kwargs): """Отправка запроса к NooLite и обработка возвращаемого ответа Отправка запроса к url с параметрами из kwargs:param url: url для запроса:type url: str:return: response от NooLite или исключение """ try: response = requests.get(url, auth=HTTPBasicAuth(self.login, self.password), timeout=self.request_timeout, **kwargs) except ConnectTimeout as e: print(e) raise NooLiteConnectionTimeout("Connection timeout: {}".format(self.request_timeout)) except ConnectionError as e: print(e) raise NooLiteConnectionError("Connection timeout: {}".format(self.request_timeout)) if response.status_code != 200: raise NooLiteBadResponse("Bad response: {}".format(response)) else: return response # Кастомные исключения NooLiteConnectionTimeout = type("NooLiteConnectionTimeout", (Exception,), {}) NooLiteConnectionError = type("NooLiteConnectionError", (Exception,), {}) NooLiteBadResponse = type("NooLiteBadResponse", (Exception,), {}) NooLiteBadRequestMethod = type("NooLiteBadRequestMethod", (Exception,), {})



telegram_bot.py

import os import logging import functools import yaml import requests import telnetlib from requests.exceptions import ConnectionError from telegram import ReplyKeyboardMarkup, ParseMode from telegram.ext import Updater, CommandHandler, Filters, MessageHandler, Job from noolite_api import NooLiteApi, NooLiteConnectionTimeout,\ NooLiteConnectionError, NooLiteBadResponse # Получаем конфигурационные данные из файла config = yaml.load(open("conf.yaml")) # Базовые настройка логирования logger = logging.getLogger() logger.setLevel(logging.INFO) formatter = logging.Formatter("%(asctime)s - %(filename)s:%(lineno)s - %(levelname)s - %(message)s") stream_handler = logging.StreamHandler() stream_handler.setFormatter(formatter) logger.addHandler(stream_handler) # Подключаемся к боту и NooLite updater = Updater(config["telegtam"]["token"]) noolite_api = NooLiteApi(config["noolite"]["login"], config["noolite"]["password"], config["noolite"]["api_url"]) job_queue = updater.job_queue def auth_required(func): """Декоратор аутентификации""" @functools.wraps(func) def wrapped(bot, update): if update.message.chat_id not in config["telegtam"]["authenticated_users"]: bot.sendMessage(chat_id=update.message.chat_id, text="Вы неавторизованы.\nДля авторизации отправьте /auth password.") else: return func(bot, update) return wrapped def log(func): """Декоратор логирования""" @functools.wraps(func) def wrapped(bot, update): logger.info("Received message: {}".format(update.message.text if update.message else update.callback_query.data)) func(bot, update) logger.info("Response was sent") return wrapped def start(bot, update): """Команда начала взаимодействия с ботом""" bot.sendMessage(chat_id=update.message.chat_id, text="Для начала работы нужно авторизоваться.\n" "Для авторизации отправьте /auth password.") def auth(bot, update): """Аутентификация Если пароль указан верно, то в ответ приходит клавиатура управления умным домом """ if config["telegtam"]["password"] in update.message.text: if update.message.chat_id not in config["telegtam"]["authenticated_users"]: config["telegtam"]["authenticated_users"].append(update.message.chat_id) custom_keyboard = [ ["/Включить_обогреватели", "/Выключить_обогреватели"], ["/Включить_прожектор", "/Выключить_прожектор"], ["/Температура"] ] reply_markup = ReplyKeyboardMarkup(custom_keyboard) bot.sendMessage(chat_id=update.message.chat_id, text="Вы авторизованы.", reply_markup=reply_markup) else: bot.sendMessage(chat_id=update.message.chat_id, text="Неправильный пароль.") def send_command_to_noolite(command): """Обработка запросов в NooLite. Отправляем запрос. Если возращается ошибка, то посылаем пользователю ответ об этом. """ try: logger.info("Send command to noolite: {}".format(command)) response = noolite_api.send_command_to_channel(command) except NooLiteConnectionTimeout as e: logger.info(e) return None, "*Дача недоступна!*\n`{}`".format(e) except NooLiteConnectionError as e: logger.info(e) return None, "*Ошибка!*\n`{}`".format(e) except NooLiteBadResponse as e: logger.info(e) return None, "*Не удалось сделать запрос!*\n`{}`".format(e) return response.text, None # ========================== Commands ================================ @log @auth_required def outdoor_light_on(bot, update): """Включения уличного прожектора""" response, error = send_command_to_noolite({"ch": 2, "cmd": 2}) logger.info("Send message: {}".format(response or error)) bot.sendMessage(chat_id=update.message.chat_id, text="{}".format(response or error)) @log @auth_required def outdoor_light_off(bot, update): """Выключения уличного прожектора""" response, error = send_command_to_noolite({"ch": 2, "cmd": 0}) logger.info("Send message: {}".format(response or error)) bot.sendMessage(chat_id=update.message.chat_id, text="{}".format(response or error)) @log @auth_required def heaters_on(bot, update): """Включения обогревателей""" response, error = send_command_to_noolite({"ch": 0, "cmd": 2}) logger.info("Send message: {}".format(response or error)) bot.sendMessage(chat_id=update.message.chat_id, text="{}".format(response or error)) @log @auth_required def heaters_off(bot, update): """Выключения обогревателей""" response, error = send_command_to_noolite({"ch": 0, "cmd": 0}) logger.info("Send message: {}".format(response or error)) bot.sendMessage(chat_id=update.message.chat_id, text="{}".format(response or error)) @log @auth_required def send_temperature(bot, update): """Получаем информацию с датчиков""" try: sens_list = noolite_api.get_sens_data() except NooLiteConnectionTimeout as e: logger.info(e) bot.sendMessage(chat_id=update.message.chat_id, text="*Дача недоступна!*\n`{}`".format(e), parse_mode=ParseMode.MARKDOWN) return except NooLiteBadResponse as e: logger.info(e) bot.sendMessage(chat_id=update.message.chat_id, text="*Не удалось получить данные!*\n`{}`".format(e), parse_mode=ParseMode.MARKDOWN) return except NooLiteConnectionError as e: logger.info(e) bot.sendMessage(chat_id=update.message.chat_id, text="*Ошибка подключения к noolite!*\n`{}`".format(e), parse_mode=ParseMode.MARKDOWN) return if sens_list.temperature and sens_list.humidity: message = "Температура: *{}C*\nВлажность: *{}%*".format(sens_list.temperature, sens_list.humidity) else: message = "Не удалось получить данные: {}".format(sens_list.state) logger.info("Send message: {}".format(message)) bot.sendMessage(chat_id=update.message.chat_id, text=message, parse_mode=ParseMode.MARKDOWN) @log @auth_required def send_log(bot, update): """Получение лога для отладки""" bot.sendDocument(chat_id=update.message.chat_id, document=open("/var/log/telegram_bot/err.log", "rb")) @log def unknown(bot, update): """Неизвестная команда""" bot.sendMessage(chat_id=update.message.chat_id, text="Я не знаю такой команды") def power_restore(bot, job): """Выполняется один раз при запуске бота""" for user_chat in config["telegtam"]["authenticated_users"]: bot.sendMessage(user_chat, "Включение после перезагрузки") def check_temperature(bot, job): """Периодическая проверка температуры с датчиков Eсли температура ниже, чем установленный минимум - посылаем уведомление зарегистрированным пользователям """ try: sens_list = noolite_api.get_sens_data() except NooLiteConnectionTimeout as e: print(e) return except NooLiteConnectionError as e: print(e) return except NooLiteBadResponse as e: print(e) return if sens_list.temperature and \ sens_list.temperature < config["noolite"]["temperature_alert"]: for user_chat in config["telegtam"]["authenticated_users"]: bot.sendMessage(chat_id=user_chat, parse_mode=ParseMode.MARKDOWN, text="*Температура ниже {} градусов: {}!*".format(config["noolite"]["temperature_alert"], sens_list.temperature)) def check_internet_connection(bot, job): """Периодическая проверка доступа в интернет Если доступа в интрнет нет и попытки его проверки исчерпаны - то посылаем по telnet команду роутеру для его перезапуска. Если доступ в интернет после этого не появился - перезагружаем Raspberry Pi """ try: requests.get("http://ya.ru") config["noolite"]["internet_connection_counter"] = 0 except ConnectionError: if config["noolite"]["internet_connection_counter"] == 2: tn = telnetlib.Telnet(config["router"]["ip"]) tn.read_until(b"login: ") tn.write(config["router"]["login"].encode("ascii") + b"\n") tn.read_until(b"Password: ") tn.write(config["router"]["password"].encode("ascii") + b"\n") tn.write(b"reboot\n") elif config["noolite"]["internet_connection_counter"] == 4: os.system("sudo reboot") else: config["noolite"]["internet_connection_counter"] += 1 dispatcher = updater.dispatcher dispatcher.add_handler(CommandHandler("start", start)) dispatcher.add_handler(CommandHandler("auth", auth)) dispatcher.add_handler(CommandHandler("Температура", send_temperature)) dispatcher.add_handler(CommandHandler("Включить_обогреватели", heaters_on)) dispatcher.add_handler(CommandHandler("Выключить_обогреватели", heaters_off)) dispatcher.add_handler(CommandHandler("Включить_прожектор", outdoor_light_on)) dispatcher.add_handler(CommandHandler("Выключить_прожектор", outdoor_light_off)) dispatcher.add_handler(CommandHandler("log", send_log)) dispatcher.add_handler(MessageHandler(, unknown)) job_queue.put(Job(check_internet_connection, 60*5), next_t=60*5) job_queue.put(Job(check_temperature, 60*30), next_t=60*6) job_queue.put(Job(power_restore, 60, repeat=False)) updater.start_polling(bootstrap_retries=-1)


Данный бот запускается на Raspberry Pi под Supervisor, который контролирует его состояние и запускает его при перезагрузке.




Схема работы бота


При запуске бот:

  • посылает зарегистрированным пользователям сообщение о том, что он включился и готов к работе;
  • мониторит подключение к интернету. В условии работы через мобильный интернет были случаи, когда он пропадал. Поэтому была добавлена периодическая проверка доступности подключения. Если заданное количество проверок заканчивается неудачей, то сначала скрипт перезагружает через telnet маршрутизатор, а потом, если это не помогло, и сам Raspberry Pi;
  • мониторит температуру внутри помещения и отправляет пользователю уведомление, если она опустилась ниже заданного порога;
  • выполняет команды от зарегистрированных пользователей.

Команды жестко прописаны в коде и включают в себя:

  • включение/выключение обогревателей;
  • включение/выключение уличного прожектора;
  • получение температуры с датчиков;
  • получение файла логов для дебага.

Пример общения с ботом:



В итоге я и все члены семьи получили довольно удобный интерфейс управления умным домом через Telegram. Все, что нужно сделать - установить телеграмм клиент на свое устройство и знать пароль для начала общения с ботом.


В итоге я могу:

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

Данная реализация на все 100% решила первоначальную задачу, была удобной и интуитивно понятной в использовании.

Заключение

Бюджет (по текущим ценам):

  • NooLite Ethernet-шлюз - 6.000 рублей
  • NooLite силовой датчик для управления нагрузкой - 1.500 рублей
  • NooLite датчик температуры и влажности - 3.000 рублей (без влажности дешевле)
  • Raspberry Pi - 4.000 рублей

На выходе у меня получилось довольно гибкая бюджетная система, которую можно легко расширять по мере необходимости (NooLite шлюз поддерживает до 32 каналов). Я и члены семьи могут с легкостью пользоваться ей без необходимости выполнять какие-то дополнительные действия: зашел в телеграмм - проверил температуру - включил обогреватели.


На самом деле данная реализация не последняя. Буквально неделю назад я подключил всю эту систему к Apple HomeKit, что позволило добавить управление через приложение для iOS "Дом" и соответствующую интеграцию с Siri для голосового управления. Но процесс реализации тянет на отдельную статью. Если сообществу будет интересна данная тема, то готов в ближайшее время подготовить еще одну статью.



Введение

Данный проект берет свое начало в 2014 году, когда передо мной встала задача обеспечить удаленное управления обогревательными приборами в своем загородном доме. Дело в том, что практически каждые выходные мы с семьей проводим на даче. И если летом мы, задержавшись по тем или иным причинам в городе, приехав в дом могли сразу лечь спать, то зимой, когда температура опускается до -30 градусов, мне приходилось тратить по 3-4 часа на протопку дома. Я видел следующие пути решения данной проблемы:

    "Неумное решение" - можно оставлять включенными обогреватели со встроенными термостатами на минимальной температуре поддержания тепла. Собственно ничего "умного" в этом решении нет, но 24/7 работающие обогревательные приборы в деревянном загородном доме не внушают доверия. Хотелось хотя бы минимального контроля над их состоянием, автоматизации и какой-нибудь обратной связи;

    GSM-розетки - данным решением пользуются мои соседи по дачному участку. Если кто-то не знаком с ними, то это просто управляемый посредством SMS команд переходник, который включается в розетку, а сам обогреватель включается в него. Не самое бюджетное решение, если нужно обеспечить обогрев целого дома - ссылка на маркет . Я вижу его как самое простое и менее трудозатратное в реализации, но имеющее минусы в процессе эксплуатации, такие как: целый ворох сим карт и работы по поддержанию их положительного баланса, так как для каждой комнаты нужен минимум один обогреватель, ограниченность и неудобства их контроля по средствам SMS;

  1. "Умный дом" - собственно решения, построенные на реализации "умного дома".

Как наиболее перспективное решение мною был выбран третий вариант и следующим вопросом на повестке дня стал - "Какую платформу для реализации выбрать?".


Уже не помню сколько я потратил время на поиски подходящих вариантов, но в итоге из бюджетных и доступных в магазинах решений я нашел системы: NooLite и CoCo (сейчас уже переименовали в Trust). При их сравнении решающую роль для меня сыграло то, что у NooLite есть открытое и задокументированное API для управления любыми его блоками. На тот момент необходимости в нем не было, но я сразу отметил, какую гибкость в дальнейшем это может дать. Да и цена у NooLite была существенно ниже. В итоге я остановил свой выбор именно на NooLite.

Реализация 1 - автоматизация NooLite

Система NooLite состоит из силовых модулей (под разные типы нагрузок), датчиков (температура, влажность, движение) и управляющего ими оборудования: радио пульты, настенные выключателей, USB-адаптеров для компьютера или Ethernet-шлюза PR1132. Все это можно использовать в различных комбинациях, соединять их между собой напрямую или управлять через usb-адаптеры или шлюз, подробнее об этом можете почитать на официальном сайте производителя.


Для моей задачи центральным элементом умного дома я выбрал Ethernet-шлюза PR1132, который будет управлять силовыми блоками и получать информацию с датчиков. Для работы Ethernet-шлюза необходимо подключить его к сети кабелем, поддержки Wi-Fi в нем нет. На тот момент у меня в доме уже была организована сеть, состоящая из WiFi-маршрутизатора Asus rt-n16 и USB--модема для доступа к интернету. Поэтому весь монтаж NooLite для меня заключался лишь в том, чтобы подключить шлюз кабелем к маршрутизатору, расположить в доме радиодатчики температуры и смонтировать силовые блоки в центральном электрощитке.


У NooLite есть ряд силовых блоков для разной подключаемой нагрузки. Самый "мощный" блок может управлять нагрузкой до 5000 Вт. Если требуется управление большей нагрузкой, как в моем случае, то можно сделать подключение нагрузки через управляемое реле, которым, в свою очередь, будет управлять силовой блок NooLite.




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



Ethernet-шлюза PR1132 и маршрутизатор Asus rt-n16



Беспроводной датчик температуры и влажности PT111



Электрощиток и силовой блок для наружного монтажа SR211 - в дальнейшем вместо этого блока я использовал блок для внутреннего монтажа и поместил его прямо в электрощитке


Ethernet-шлюз PR1132 имеет web-интерфейс через которой осуществляется привязка/отвязка силовых блоков, датчиков и управление ими. Сам интерфейс выполнен в довольно "топорном" минималистическом стиле, но этого вполне достаточно для доступа ко всему необходимому функционалу системы:




Настройки




Управление




Страница одной группы выключателей


Подробно о привязке и настройке всего этого - опять же на официальном сайте.


На тот момент я мог:

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

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

Реализация 2 - удаленный доступ к умному дому

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


Как я упомянул в предыдущем разделе - дачная сеть имеет доступ к интернету через usb модем одного из мобильных операторов. По умолчанию мобильные модемы имеют серый ip адрес и без дополнительных ежемесячных трат белого фиксированного ip не получить. При таком сером IP не помогут и различные no-ip сервисы.


Единственный вариант, который мне удалось на тот момент придумать - VPN. На городском маршрутизаторе у меня был настроен VPN-сервер, которым я время от времени пользовался. Мне было необходимо настроить на дачном роутере VPN-клиент и прописать статические маршруты до дачной сети.




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


В результате дачный роутер постоянно держал VPN соединение с городским роутером и для доступа к шлюзу NooLite мне нужно было с клиентского устройства (ноутбук, телефон) подключится по VPN к городскому маршрутизатору.


На этом этапе я мог:

  • получить доступ к умному дому из любого места;

В целом это практически на 100% покрывало первоначальную задачу. Однако я понимал, что данная реализация далека от оптимальной и удобной в использовании, так как каждый раз я должен был выполнять ряд дополнительных действий по подключению к VPN. Для меня это не было особой проблемой, однако для остальных членов семьи это было не очень удобно. Так же в этой реализации было очень много посредников, что сказывалось на отказоустойчивости всей системы в целом. Однако на некоторое время я остановился именно на этом варианте.

Реализация 3 - Telegram bot

С появлением ботов в Telegram я взял на заметку, что это смогло бы стать довольно удобным интерфейсом для управления умным домом и, как только у меня появилось достаточно свободного времени, я приступил к разработке на Python 3.


Бот должен был где-то находится и, как самое энергоэффективное решение, я выбрал Raspberry Pi. Хоть это и был мой первый опыт работы с ним, особых сложностей в его настройке не возникло. Образ на карту памяти, ethernet кабель в порт и по ssh - полноценный Linux.


Как я уже говорил - у NooLite есть задокументированное API, которое и пригодилось мне на данном этапе. Для начала я написал простенькую обертку для более удобного взаимодействия с API:


noolite_api.py

""" NooLite API wrapper """ import requests from requests.auth import HTTPBasicAuth from requests.exceptions import ConnectTimeout, ConnectionError import xml.etree.ElementTree as ET class NooLiteSens: """Класс хранения и обработки информации, полученной с датчиков Пока как таковой обработки нет """ def __init__(self, temperature, humidity, state): self.temperature = float(temperature.replace(",", ".")) if temperature != "-" else None self.humidity = int(humidity) if humidity != "-" else None self.state = state class NooLiteApi: """Базовый враппер для общения с NooLite""" def __init__(self, login, password, base_api_url, request_timeout=10): self.login = login self.password = password self.base_api_url = base_api_url self.request_timeout = request_timeout def get_sens_data(self): """Получение и прасинг xml данных с датчиков:return: список NooLiteSens объектов для каждого датчика:rtype: list """ response = self._send_request("{}/sens.xml".format(self.base_api_url)) sens_states = { 0: "Датчик привязан, ожидается обновление информации", 1: "Датчик не привязан", 2: "Нет сигнала с датчика", 3: "Необходимо заменить элемент питания в датчике" } response_xml_root = ET.fromstring(response.text) sens_list = for sens_number in range(4): sens_list.append(NooLiteSens(response_xml_root.find("snst{}".format(sens_number)).text, response_xml_root.find("snsh{}".format(sens_number)).text, sens_states.get(int(response_xml_root.find("snt{}".format(sens_number)).text)))) return sens_list def send_command_to_channel(self, data): """Отправка запроса к NooLite Отправляем запрос к NooLite с url параметрами из data:param data: url параметры:type data: dict:return: response """ return self._send_request("{}/api.htm".format(self.base_api_url), params=data) def _send_request(self, url, **kwargs): """Отправка запроса к NooLite и обработка возвращаемого ответа Отправка запроса к url с параметрами из kwargs:param url: url для запроса:type url: str:return: response от NooLite или исключение """ try: response = requests.get(url, auth=HTTPBasicAuth(self.login, self.password), timeout=self.request_timeout, **kwargs) except ConnectTimeout as e: print(e) raise NooLiteConnectionTimeout("Connection timeout: {}".format(self.request_timeout)) except ConnectionError as e: print(e) raise NooLiteConnectionError("Connection timeout: {}".format(self.request_timeout)) if response.status_code != 200: raise NooLiteBadResponse("Bad response: {}".format(response)) else: return response # Кастомные исключения NooLiteConnectionTimeout = type("NooLiteConnectionTimeout", (Exception,), {}) NooLiteConnectionError = type("NooLiteConnectionError", (Exception,), {}) NooLiteBadResponse = type("NooLiteBadResponse", (Exception,), {}) NooLiteBadRequestMethod = type("NooLiteBadRequestMethod", (Exception,), {})



telegram_bot.py

import os import logging import functools import yaml import requests import telnetlib from requests.exceptions import ConnectionError from telegram import ReplyKeyboardMarkup, ParseMode from telegram.ext import Updater, CommandHandler, Filters, MessageHandler, Job from noolite_api import NooLiteApi, NooLiteConnectionTimeout,\ NooLiteConnectionError, NooLiteBadResponse # Получаем конфигурационные данные из файла config = yaml.load(open("conf.yaml")) # Базовые настройка логирования logger = logging.getLogger() logger.setLevel(logging.INFO) formatter = logging.Formatter("%(asctime)s - %(filename)s:%(lineno)s - %(levelname)s - %(message)s") stream_handler = logging.StreamHandler() stream_handler.setFormatter(formatter) logger.addHandler(stream_handler) # Подключаемся к боту и NooLite updater = Updater(config["telegtam"]["token"]) noolite_api = NooLiteApi(config["noolite"]["login"], config["noolite"]["password"], config["noolite"]["api_url"]) job_queue = updater.job_queue def auth_required(func): """Декоратор аутентификации""" @functools.wraps(func) def wrapped(bot, update): if update.message.chat_id not in config["telegtam"]["authenticated_users"]: bot.sendMessage(chat_id=update.message.chat_id, text="Вы неавторизованы.\nДля авторизации отправьте /auth password.") else: return func(bot, update) return wrapped def log(func): """Декоратор логирования""" @functools.wraps(func) def wrapped(bot, update): logger.info("Received message: {}".format(update.message.text if update.message else update.callback_query.data)) func(bot, update) logger.info("Response was sent") return wrapped def start(bot, update): """Команда начала взаимодействия с ботом""" bot.sendMessage(chat_id=update.message.chat_id, text="Для начала работы нужно авторизоваться.\n" "Для авторизации отправьте /auth password.") def auth(bot, update): """Аутентификация Если пароль указан верно, то в ответ приходит клавиатура управления умным домом """ if config["telegtam"]["password"] in update.message.text: if update.message.chat_id not in config["telegtam"]["authenticated_users"]: config["telegtam"]["authenticated_users"].append(update.message.chat_id) custom_keyboard = [ ["/Включить_обогреватели", "/Выключить_обогреватели"], ["/Включить_прожектор", "/Выключить_прожектор"], ["/Температура"] ] reply_markup = ReplyKeyboardMarkup(custom_keyboard) bot.sendMessage(chat_id=update.message.chat_id, text="Вы авторизованы.", reply_markup=reply_markup) else: bot.sendMessage(chat_id=update.message.chat_id, text="Неправильный пароль.") def send_command_to_noolite(command): """Обработка запросов в NooLite. Отправляем запрос. Если возращается ошибка, то посылаем пользователю ответ об этом. """ try: logger.info("Send command to noolite: {}".format(command)) response = noolite_api.send_command_to_channel(command) except NooLiteConnectionTimeout as e: logger.info(e) return None, "*Дача недоступна!*\n`{}`".format(e) except NooLiteConnectionError as e: logger.info(e) return None, "*Ошибка!*\n`{}`".format(e) except NooLiteBadResponse as e: logger.info(e) return None, "*Не удалось сделать запрос!*\n`{}`".format(e) return response.text, None # ========================== Commands ================================ @log @auth_required def outdoor_light_on(bot, update): """Включения уличного прожектора""" response, error = send_command_to_noolite({"ch": 2, "cmd": 2}) logger.info("Send message: {}".format(response or error)) bot.sendMessage(chat_id=update.message.chat_id, text="{}".format(response or error)) @log @auth_required def outdoor_light_off(bot, update): """Выключения уличного прожектора""" response, error = send_command_to_noolite({"ch": 2, "cmd": 0}) logger.info("Send message: {}".format(response or error)) bot.sendMessage(chat_id=update.message.chat_id, text="{}".format(response or error)) @log @auth_required def heaters_on(bot, update): """Включения обогревателей""" response, error = send_command_to_noolite({"ch": 0, "cmd": 2}) logger.info("Send message: {}".format(response or error)) bot.sendMessage(chat_id=update.message.chat_id, text="{}".format(response or error)) @log @auth_required def heaters_off(bot, update): """Выключения обогревателей""" response, error = send_command_to_noolite({"ch": 0, "cmd": 0}) logger.info("Send message: {}".format(response or error)) bot.sendMessage(chat_id=update.message.chat_id, text="{}".format(response or error)) @log @auth_required def send_temperature(bot, update): """Получаем информацию с датчиков""" try: sens_list = noolite_api.get_sens_data() except NooLiteConnectionTimeout as e: logger.info(e) bot.sendMessage(chat_id=update.message.chat_id, text="*Дача недоступна!*\n`{}`".format(e), parse_mode=ParseMode.MARKDOWN) return except NooLiteBadResponse as e: logger.info(e) bot.sendMessage(chat_id=update.message.chat_id, text="*Не удалось получить данные!*\n`{}`".format(e), parse_mode=ParseMode.MARKDOWN) return except NooLiteConnectionError as e: logger.info(e) bot.sendMessage(chat_id=update.message.chat_id, text="*Ошибка подключения к noolite!*\n`{}`".format(e), parse_mode=ParseMode.MARKDOWN) return if sens_list.temperature and sens_list.humidity: message = "Температура: *{}C*\nВлажность: *{}%*".format(sens_list.temperature, sens_list.humidity) else: message = "Не удалось получить данные: {}".format(sens_list.state) logger.info("Send message: {}".format(message)) bot.sendMessage(chat_id=update.message.chat_id, text=message, parse_mode=ParseMode.MARKDOWN) @log @auth_required def send_log(bot, update): """Получение лога для отладки""" bot.sendDocument(chat_id=update.message.chat_id, document=open("/var/log/telegram_bot/err.log", "rb")) @log def unknown(bot, update): """Неизвестная команда""" bot.sendMessage(chat_id=update.message.chat_id, text="Я не знаю такой команды") def power_restore(bot, job): """Выполняется один раз при запуске бота""" for user_chat in config["telegtam"]["authenticated_users"]: bot.sendMessage(user_chat, "Включение после перезагрузки") def check_temperature(bot, job): """Периодическая проверка температуры с датчиков Eсли температура ниже, чем установленный минимум - посылаем уведомление зарегистрированным пользователям """ try: sens_list = noolite_api.get_sens_data() except NooLiteConnectionTimeout as e: print(e) return except NooLiteConnectionError as e: print(e) return except NooLiteBadResponse as e: print(e) return if sens_list.temperature and \ sens_list.temperature < config["noolite"]["temperature_alert"]: for user_chat in config["telegtam"]["authenticated_users"]: bot.sendMessage(chat_id=user_chat, parse_mode=ParseMode.MARKDOWN, text="*Температура ниже {} градусов: {}!*".format(config["noolite"]["temperature_alert"], sens_list.temperature)) def check_internet_connection(bot, job): """Периодическая проверка доступа в интернет Если доступа в интрнет нет и попытки его проверки исчерпаны - то посылаем по telnet команду роутеру для его перезапуска. Если доступ в интернет после этого не появился - перезагружаем Raspberry Pi """ try: requests.get("http://ya.ru") config["noolite"]["internet_connection_counter"] = 0 except ConnectionError: if config["noolite"]["internet_connection_counter"] == 2: tn = telnetlib.Telnet(config["router"]["ip"]) tn.read_until(b"login: ") tn.write(config["router"]["login"].encode("ascii") + b"\n") tn.read_until(b"Password: ") tn.write(config["router"]["password"].encode("ascii") + b"\n") tn.write(b"reboot\n") elif config["noolite"]["internet_connection_counter"] == 4: os.system("sudo reboot") else: config["noolite"]["internet_connection_counter"] += 1 dispatcher = updater.dispatcher dispatcher.add_handler(CommandHandler("start", start)) dispatcher.add_handler(CommandHandler("auth", auth)) dispatcher.add_handler(CommandHandler("Температура", send_temperature)) dispatcher.add_handler(CommandHandler("Включить_обогреватели", heaters_on)) dispatcher.add_handler(CommandHandler("Выключить_обогреватели", heaters_off)) dispatcher.add_handler(CommandHandler("Включить_прожектор", outdoor_light_on)) dispatcher.add_handler(CommandHandler("Выключить_прожектор", outdoor_light_off)) dispatcher.add_handler(CommandHandler("log", send_log)) dispatcher.add_handler(MessageHandler(, unknown)) job_queue.put(Job(check_internet_connection, 60*5), next_t=60*5) job_queue.put(Job(check_temperature, 60*30), next_t=60*6) job_queue.put(Job(power_restore, 60, repeat=False)) updater.start_polling(bootstrap_retries=-1)


Данный бот запускается на Raspberry Pi под Supervisor, который контролирует его состояние и запускает его при перезагрузке.




Схема работы бота


При запуске бот:

  • посылает зарегистрированным пользователям сообщение о том, что он включился и готов к работе;
  • мониторит подключение к интернету. В условии работы через мобильный интернет были случаи, когда он пропадал. Поэтому была добавлена периодическая проверка доступности подключения. Если заданное количество проверок заканчивается неудачей, то сначала скрипт перезагружает через telnet маршрутизатор, а потом, если это не помогло, и сам Raspberry Pi;
  • мониторит температуру внутри помещения и отправляет пользователю уведомление, если она опустилась ниже заданного порога;
  • выполняет команды от зарегистрированных пользователей.

Команды жестко прописаны в коде и включают в себя:

  • включение/выключение обогревателей;
  • включение/выключение уличного прожектора;
  • получение температуры с датчиков;
  • получение файла логов для дебага.

Пример общения с ботом:



В итоге я и все члены семьи получили довольно удобный интерфейс управления умным домом через Telegram. Все, что нужно сделать - установить телеграмм клиент на свое устройство и знать пароль для начала общения с ботом.


В итоге я могу:

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

Данная реализация на все 100% решила первоначальную задачу, была удобной и интуитивно понятной в использовании.

Заключение

Бюджет (по текущим ценам):

  • NooLite Ethernet-шлюз - 6.000 рублей
  • NooLite силовой датчик для управления нагрузкой - 1.500 рублей
  • NooLite датчик температуры и влажности - 3.000 рублей (без влажности дешевле)
  • Raspberry Pi - 4.000 рублей

На выходе у меня получилось довольно гибкая бюджетная система, которую можно легко расширять по мере необходимости (NooLite шлюз поддерживает до 32 каналов). Я и члены семьи могут с легкостью пользоваться ей без необходимости выполнять какие-то дополнительные действия: зашел в телеграмм - проверил температуру - включил обогреватели.


На самом деле данная реализация не последняя. Буквально неделю назад я подключил всю эту систему к Apple HomeKit, что позволило добавить управление через приложение для iOS "Дом" и соответствующую интеграцию с Siri для голосового управления. Но процесс реализации тянет на отдельную статью. Если сообществу будет интересна данная тема, то готов в ближайшее время подготовить еще одну статью.



Перечень достижений человека постоянно пополняется новыми разработками. Выдающейся можно назвать разработанную человеком популярную сегодня опцию «Умный дом» на мини-компьютере Raspberry Pi.

Упоминаемая система после своего выхода на рынок практически сразу влюбила в себя многих владельцев домов. Именно поэтому спрос на систему «умный дом» на Raspberry pi стал активно возрастать с каждым годом. Хотите выяснить, чем так уникален Raspberry pi 3 умный дом и почему именно эту систему сегодня во многих домах устанавливают? Изучите нижеизложенный материал.

Что представляют собой проекты Raspberry pi 3 для дома и зачем они нужны?

Система «умный дом», которую изобрела компания Raspberry Pi, позволяет контролировать абсолютно все в доме: начиная от включения света и отопительной системы и заканчивая активацией систем, которые имитируют присутствие в доме человека.

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

Система умный дом от известной многим компании «Raspberry Pi» столь востребована сегодня благодаря тому, что она может легко собираться, как простой конструктор. Функционировать он может при этом на разных ядрах. Одной из наиболее покупаемых сегодня является система, функционирующая на базе мини-компьютера Raspberry.

Изначально компания предложила две комплектации мини-компьютеров - модель А и модель В. Модель А располагала объемом памяти в 256 Мб, а модель В могла сохранять в два раза больше информации. Но модель с небольшим объемом памяти не сняли с производства потому, что она позволяла пользователю выходить в Интернет. Позже была обновлена модель В. Новый её вариант отличался более компактным дизайном и располагал 4 USB-портами.

Как система функционирует?

Структура системы может быть разной. Но обычно построение её происходит по такому принципу: главным является центральное вычислительное устройство (его еще называют сервером). В качестве центрального системного сервера выступает Raspberry Pi , на который устанавливается WEB-интерфейс. Последний может легко связываться с ноутбуком, планшетом либо же смартфоном.

Сервер связывается с периферийными спецмодулями посредством RS485 - интерфейса. Чтобы система слаженно работала, в каждом ключевом помещении домовладения всегда устанавливают спецконтроллер, задача которого - интерпретировать поступающие сигналы и отправлять их на устройства бытовой техники, которые в этой связи являются устройствами-исполнителями.

Обычно модуль Raspberry Pi связывается с контроллерами через порт UART. К последнему следует подключать такое согласующее устройство, как спецпереходник на интерфейс RS485 (следует подчеркнуть, что последние модели устройства уже располагают этим интерфейсом, так как его подразумевает базовая комплектация).

«Операционкой» является Raspberry, спутником которой может быть расширение, например, Pimatic. Совсем несложно собрать спецсистему «умный дом» на «открытой платформе», к примеру, openHAB, Fhem, SHC. Также популярной продолжает оставаться платформа wiButler.

Модули, которые можно выбрать для выстраиваемой своими руками системы «умный дом»

Чтобы выстроить спецсистему и сделать ее максимально функциональной, потребуются особые модули. Для тех, кто любит мастерить и хочет создать Raspberry pi 3 умный дом своими руками, предлагаются сегодня такие модули, как:

  • Модуль камеры. Подключив его, систему можно рассматривать, как удобный инструмент для видеонаблюдения. Данная камера совместима с «операционкой» мини-компьютера Raspberry. Она позволит записывать видеофрагменты в разрешении Full HD и делать отличные 5-мегапиксельные фото.
  • Датчик, измеряющий влажность, а также температуру воздуха. При установке этого модуля, система будет обрабатывать метеоданные.

  • Датчики дыма, а также датчики, сигнализирующие о протечке воды. Данный установленный и подключенный модуль поможет своевременно реагировать на появление дыма в доме и выявлять протечку воды. Многие владельцы больших коттеджей выбирают эту опцию для того, чтобы максимально обезопасить свой дом от неприятных сюрпризов.
  • Датчик движения, совместимый с Raspberry Pi. Данный модуль возможно использовать для того, чтоб лампы домашние включались при малейших замеченных движениях в любой зоне домовладения.

Преимущества Raspberry Pi 3 по сравнению с ранее выпущенными версиями

Преимуществ у последней прогрессивной версии Raspberry Pi 3 много:

  • Устройство имеет много интерфейсов. Последняя версия располагает Bluetooth 4.1, Wi-Fi 802.11n, Lan, 4 USB, а также HDMI. Также имеется возможность подключить GSM-модем для выхода на связь с официальным мобильным оператором, который занимается предоставлением услуг интернет-доступа.
  • Raspberry Pi 3 имеет мощный четырехъядерный 1,2 ГГц процессор.
  • Последняя версия совместима с предыдущими.

Плюсы и минусы системы «умный дом» от фирмы Raspberry Pi

Для человека, который желает сделать свою жизнь комфортнее и безопаснее, система «умный дом» Raspberry Pi 3 является разумным решением. Подводя итог всему вышесказанному, можно выделить четыре важных преимуществ «умного дома».

  • Возможность максимально обезопасить свой дом за счет контроля протечек, установленного видеонаблюдения, охранной, а также противопожарной системы.
  • Возможность установки системы, помогающей экономить (речь идет о датчиках движения, смесителях сенсорного типа, датчиках, фиксирующих движение).
  • Возможность установки систем, повышающих уровень комфорта (спецсистемы управления шторами, электроприборами).
  • Возможность установки интеллектуальных развлекательных спецсистем (речь идет о системе мультирум, спецсистеме «домашний кинотеатр»).

Наряду с плюсами, есть у системы «умный дом» и несколько незначительных минусов:

  1. Систему целесообразно покупать только для большого загородного дома.
  2. Важно разбираться в каждом элементе системы (или иметь всегда под боком разбирающегося в этом вопросе человека), так как из-за своей сложности система в любой момент может выйти из строя.
  3. Так как прогресс галопирующими темпами продвигается вперед, через пять-семь лет и эта прогрессивная система устареет, поэтому есть риск того, что с продажи могут исчезнуть важные ее компоненты.

Как видим, плюсы уверенно перекрывают минусы. Поэтому если есть желание купить систему «умный дом» для загородного коттеджа, не стоит отказываться от этой идеи.