Простые системы умный дом raspberry. Умный дом на Raspberry Pi и Arduino. В чем отличия Raspberry Pi от Arduino

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

Структурная схема системы «Умный Дом» на базе Raspberry Pi 3

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

Основные характеристики Raspberry Pi 3

Новый микрокомпьютер получил:

  1. процессор ARM Cortex-A53;
  2. оперативную память в один гигабайт;
  3. встроенные WiFi и Bluetooth 4.1;
  4. полную совместимость с предыдущими моделями.

Процессор имеет четыре ядра, разрядность 64 бита, частоту 1.2 ГГц, что в 10 раз превышает производительность первой модели.

WiFi стандарта 802.11b/g/n с возможностью передачи данных со скоростью до 600 Мб/сек на частоте 5.0 ГГц.

По заказу «Raspberry Pi» компания «Broadcom» разработала и выпустила новый монокристалл BCM2837 с архитектурой, аналогичной кристаллам BCM2835 и BCM2836, что обеспечило совместимость с предыдущими моделями.

Одноплатные компьютеры Raspberry Pi 3 и Arduino – главные отличия

Сравнение этих платформ не совсем корректно. Прежде всего потому, что Raspberry Pi 3 по своему наполнению является компьютером с полным набором функций, а Arduino отнести к классу компьютеров достаточно проблематично.

Raspberry Pi 3 оснащен мощным четырёхъядерным процессором, частота которого в 40 раз выше тактовой частоты Arduino. Оперативная память Raspberry Pi 3 имеет емкость, в 128000 раз превышающую емкость оперативной памяти Arduino.

Но эти показатели говорят не о том, что Raspberry Pi 3 превосходит Arduino, а о том, что эти устройства предназначены для решения разных задач.

Задачи, решаемые Raspberry Pi 3 с помощью ПО, не под силу решать простому Arduino. Но он превосходно справляется с задачами сугубо аппаратных проектов, со считыванием и обработкой аналоговых сигналов.

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

Реализация проекта «Умный дом» на базе Raspberry Pi 3

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

Периферийные устройства «умного дома»

Для реализации проекта Raspberry Pi 3 «Умный дом» понадобятся такие модули:

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

Кроме этого, для системы Raspberry Pi 3 «Умный дом» потребуются карта памяти MicroSD емкостью 32 ГГб, блок питания, пятивольтовое реле.

Одним из важнейших дополнительных устройств для реализации проекта Raspberry «Умный дом» является модуль NodeMCU ESP-12E с радиоинтерфейсом Wi-Fi и интегрированной в плату модуля микрополосковой антенной.

Операционные системы

Как и любой компьютер, Raspberry Pi 3 без программного обеспечения представляет собой просто бесполезный набор электронных элементов.

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

Этот процесс выполняется в несколько этапов. Вначале необходимо выбрать и загрузить для Raspberry Pi 3 «Умный дом» iOS – мобильную операционную систему.

В качестве носителя для ОС и размещения на нем программ в Raspberry Pi 3 используется микрокарта памяти SD. Для установки ОС можно выбрать один из трех способов:

  1. купить SD-карту, на которую уже была предварительно установлена ОС;
  2. загрузить на карту памяти NOOBS (New Out Of the Box Software) – установщик ОС, и затем устанавливать ОС прямо с карты;
  3. монтировать образ ОС прямо на карту SD.

Для системы Raspberry «Умный дом» разработано более 40 различных ОС. Чаще всего используются ОС Raspbian OS, Ubuntu Mate, Windows 10 IoT, RICS OS.

Наиболее адаптированной под аппаратные средства Raspberry Pi 3 явл

яется операционная система Raspbian OS, устанавливаемая с загруженного на SD-карту установщика NOOBS.

Установка операционной системы

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

Для самого первого запуска понадобятся:

  • микрокарта SD, емкостью не менее четырех гигабайт (предпочтительно 32 гигабайта);
  • блок питания на пять вольт;
  • кабель с разъемами HDMI;
  • монитор с HDMI-подключением;
  • клавиатура и мышь с USB-подключением;
  • компьютер с разъемом для SD-карты;
  • подключение к интернету – Ethernet.

Следующие действия таковы:

  1. форматирование SD-карты;
  2. скачивание архива установщика NOOBS и распаковка его в корневую директорию SD-карты;
  3. карта вставляется в слот микрокомпьютера, подключаются все устройства, включается блок питания;
  4. при первом запуске из списка выбирается нужная ОС и запускается ее установка;
  5. по завершении установки установить и настроить программы Raspberry Pi 3 «Умный дом».

Установка сервера Homebridge и настройка модулей

Система «Умный дом» работает с технологией Home Kit, объединяющей все устройства «умного дома» в одном приложении, и воспринимающей голосовые команды, поданные на русском языке. Но таких устройств, особенно «понимающих» русский язык, не так уж и много, к тому же, они очень дорогие.

Сервер Homebridge выполняет роль своеобразного мостика между всеми устройствами дома и Home Kit. Этот сервер эмулирует Home Kit API.

Серверу доступны сотни самых различных плагинов, благодаря которым стало возможным осуществлять управление всеми домашними устройствами, которые даже конструктивно не предназначены для работы с Home Kit. Главное преимущество Homebridge то, что он может работать на любом компьютере, в том числе и на Raspberry Pi 3.

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

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

Заключение

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

Видео: Raspberry Pi Model 3 B — устанавливаем систему управления умным домом Domoticz

Система очень простая, она позволяет управлять по сети (внешней или локальной) пинами GPIO настроенными на выход и получать их статус (то есть видеть, что включено-выключено) . Отправлять какие-либо команды, которые можно обрабатывать внутри Raspberry. Получать состояние пинов GPIO (в виде 0 или 1) настроенных на вход. Конструктор сам настроет нужные вам пины GPIO на вход или на выход.

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

Пользователю нужно только лишь собрать интерфейс в конструкторе, скачать архив с файлами и рапаковать его на своём RaspberryPi 2 .
В архиве будут лежать готовые файлы относящихся к веб-части (html, css, js) , sh-скрипт для инициализации пинов и сервер для обмена данными Home stD Rp . Установка каких-либо дополнительных программ не требуется.

Несмотря на, может быть, покажущуюся сложность, все очень просто и займёт минут 10-15.

Внешний вид

Ознакомительная часть.

Как уже говорилось в предыдущей , идея web-интерфейса достаточно проста и в чём-то даже аскетична. Связано это с тем, что мне разонравились нарисованные комнаты с лампочками и прочие полумеры, поэтому выбран стиль «пульта от телевизора».

Работает на любом устройстве - компьютере, ноутбуке, планшете, мобильнике.


Главный экран интерфейса. Помещений может быть до пяти.

На основном экране расположены кнопки с названиями помещений, нажатие на которые открывает панель с органами управления соответствующим помещением.

Откроем прихожую:

Здесь могут располагаться: несколько кнопок настроенных на выход (BCM0 , BCM1 и т.д.) для включения чего-либо с возвратом статуса. То есть, если это что-то (например верхний свет) включено, то надпись подсветится.

Несколько кнопок (SENTSIG1 и т.д) для отправки любой команды в RaspberryPi.

И несколько полей (IN_BCM2 и т.д) для получения состояние пинов GPIO (в виде 0 или 1) настроенных на вход. 0 - пин подтянут к «земле», 1 - на пине есть напряжение.

В дальнейшем можно изменять названия кнопок и менять их местами.

рестик справа-сверху - закрытие панели.

Под кнопкой Info скрывается панель с информацией о статусе системы.

Надпись Connect! говорит о том, что всё хорошо, а Count update: - это просто счётчик запросов (браузер с интервалом в ~700мс запрашивает у ардуины данные) . Интервал можно менять.

Если произойдёт какая-то неполадка, тогда на экране появится сообщение ERROR , а в Info будет описана причина ошибки.

Весь алгоритм работы системы описан в конце статьи.

Конец ознакомительной части.

Конструктор

Коротенькое видео по работе с конструктором, можно посмотреть вот по этой ссылке .

«Умный дом» будет работать на любом устройстве, а вот конструировать надо на обычном компе или ноуте.

В браузере должны быть включены cookie. Впрочем они и так почти у всех включены.

Конструктор предельно прост и интуитивно понятен . Открыв в соседней вкладке вот , вы окажитесь на первой странице (всего их четыре) :

Чтоб понять и потренироваться, проделайте всё как написано ниже.

Здесь нужно выбрать количество помещений (максимум 5) . Предположим, что у нас будет два помещения (прихожая и кухня) , тогда выберите 2 и нажмите «Далее».

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

В дальнейшем Вы можете это исправить в файле index.html.

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

В поля Адрес сервера и Порт сервера ничего писать не нужно (сделано на будущее) .

Названия помещений у нас уже придуманы (прихожая и кухня) , вписываем их и нажимаем кнопку «Далее»…

Здесь Вы увидите главный экран своего будущего интерфейса:

Нажмите на кнопку «Прихожая»…

Выберите две кнопки для включения чего-либо с возвратом статуса (Количество кнопок вкл/откл) .
Одну кнопку для отправки команды (Количество кнопок отправки сигнала) .
И одно поле для приёма статуса с каких-либо пинов (Количество полей для приёма информации) .

Максимум можно выбрать по пять кнопок.

Теперь закройте панель кнопкой , проделайте то же самое с «Кухней» и нажмите кнопку «Далее»…

Появится главный экран с кнопкой «Скачать архив»:

На этом работа с конструктором закончена, нажмите и переходите к следующей части.

Внимание! У Raspbery Pi 2 всего 28 GPIO (BCM0 - BCM27). Если Вы в конструкторе сделаете больше 27 кнопок/полей приёма, то всем лишним BCM будет присваиваться номер 28 , а у лишних полей приёма IN_BCM значение будет просто увеличиваться. Функционировать они не будут.

Подключение
Соберём простенькую схему для испытания системы:

Подключите светодиод к пину BCM 1 , через резистор 500-1000 Ом. При нажатии в интерфейсе кнопки BCM1, светик будет загораться/гаснуть, а надпись менять цвет.

Так же подключите проводок через резистор 500-1000 Ом к BCM 3 , этим проводочком можно будет тыкать на +3.3 или Ground , и в поле IN_BCM3 нолик на единичку. Таким образом можно отслеживать какие-то события.

BCM 2 используйте только на выход . Если сделали как вход (IN_BCM2) , то не пользуйтесь им или переделайте как выход (BCM2) .

Применение кнопок SENTSIG описано ниже.

HomestDRp
Распаковав архив, у Вас появится папка - mydomrpXXXXXXXXXX , переименуйте её так, чтоб получилось mydomrp и перейдите в неё.

Переименуйте файлы indexXXXXXXXXX.html в index.html и initXXXXXXXXX.sh в init.sh .

В папке mydomrp получатся файлы index.html , init.sh , jquery.js и style.css и программа homestdrp .

Откройте файл index.html и в двенадцатой строчке - var flagobnov = 0 , переправьте нолик на единичку - var flagobnov = 1 (отключено в конструкторе). Сохраните файл.

Дополнительные пояснения к файлам, даны в конце статьи.

Теперь к программе homestdrp…

homestdrp - это web-сервер, который принимает запросы от клиента, передаёт команды RaspberryPi, считывает состояние пинов GPIO и отправляет обратно информацию/статус web-клиенту. Иными словами, его назначение - это обмен данными между web-клиентом (браузер) и RaspberryPi. Работает по протоколу ТСР, а в перспективе и по UDP.

Исходник

#include #include #include #include #include #include #include #include #include #include #include #include #include char response = "HTTP/1.1 200 OK\r\n" "Content-Type: text/html; charset=UTF-8\r\n\r\n"; char response_css = "HTTP/1.1 200 OK\r\n" "Content-Type: text/css; charset=UTF-8\r\n\r\n"; char response_js = "HTTP/1.1 200 OK\r\n" "Content-Type: text/js; charset=UTF-8\r\n\r\n"; char response_text = "HTTP/1.1 200 OK\r\n" "Content-Type: text/text; charset=UTF-8\r\n\r\n"; char response_403 = "HTTP/1.1 200 OK\r\n" "Content-Type: text/html; charset=UTF-8\r\n\r\n" "403" "" "

403

\r\n"; #define BUFSIZE 1024 #define ARRAY_SIZE 90000 #define BSIZ 512 char send1_array = {0,}; char send2_array = {0,}; char patch_to_dir = {0,}; char fpfile = {0,}; char buffer = {0,}; int count_simvol = 0; unsigned int PORTW = 0; int len_dat = 0; int count_warning_log =0; void error_log(char *my_error) { char file_error_log = {0,}; snprintf(file_error_log, 26, "%s", "/var/log/ErrorhomeRp.log"); time_t t; time(&t); FILE *f; f = fopen(file_error_log, "a"); if(f == NULL) { printf("Error open /var/log/ErrorhomeRp.log.\n"); exit(0); } fprintf(f, "%s", ctime(&t)); fprintf(f, "Error %s\n\n", my_error); printf("Error %s Write to /var/log/ErrorhomestdRp.log.\n", my_error); fclose(f); exit(0); } void warning_access_log(char *war_ac) { count_warning_log++; char file_Warning_Access_log = {0,}; snprintf(file_Warning_Access_log, 31, "%s", "/var/log/Warning_AccessRp.log"); if(count_warning_log > 100) { system("gzip -f /var/log/Warning_AccessRp.log"); count_warning_log =0; time_t t; time(&t); FILE *f; f = fopen(file_Warning_Access_log, "w"); fprintf(f, "%s", ctime(&t)); fprintf(f, "%s\n\n", war_ac); printf("Write to /var/log/Warning_AccessRp.log:\n%s\n", war_ac); fclose(f); } else { time_t t; time(&t); FILE *f; f = fopen(file_Warning_Access_log, "a"); fprintf(f, "%s", ctime(&t)); fprintf(f, "%s\n\n", war_ac); printf("Write to /var/log/Warning_AccessRp.log:\n%s\n", war_ac); fclose(f); } } void read_in_file(char *name_file) { count_simvol = 0; memset(send1_array, 0, ARRAY_SIZE * sizeof(char)); memset(fpfile, 0, 64 * sizeof(char)); snprintf(fpfile, (int)strlen(patch_to_dir) + (int)strlen(name_file) + 1, "%s%s", patch_to_dir, name_file); FILE *file; file = fopen(fpfile,"r"); if(file == NULL) error_log("open file."); int ch; while(ch = getc(file), ch != EOF) { send1_array = (char) ch; count_simvol++; if(count_simvol == ARRAY_SIZE - 2) break; } fclose(file); } int main(int argc, char *argv) { if(argc != 4) error_log("not argumets."); PORTW = strtoul(argv, NULL, 0); // порт для web-сервера 80 strncpy(patch_to_dir, argv, 63); // путь к папке len_dat = atoi(argv); // кол-во символов warning_access_log("START"); ////////////////////////////////////////////// WEB /////////////////////////////////////// int one = 1, client_fd; struct sockaddr_in svr_addr, cli_addr; socklen_t sin_len = sizeof(cli_addr); int sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (sock < 0) error_log("not socket."); setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(int)); svr_addr.sin_family = AF_INET; svr_addr.sin_addr.s_addr = INADDR_ANY; svr_addr.sin_port = htons(PORTW); if(bind(sock, (struct sockaddr *) &svr_addr, sizeof(svr_addr)) == -1) { close(sock); error_log("bind."); } if(listen(sock, 10) == -1) { close(sock); error_log("listen."); } ////////////////////////////////////////// work file ////////////////////////////////////////////////// char flin_fldb = {0,}; snprintf(flin_fldb, (int)strlen(patch_to_dir) + (int)strlen("init.sh 1 "), "%s%s", patch_to_dir, "init.sh 1"); system(flin_fldb); char bufRec = {0,}; char To_GPIO = {0,}; FILE *mf; char in_data = {0,}; char gp_file = {0,}; printf("Receive data from RPi\n\n"); for(;;) { client_fd = accept(sock, (struct sockaddr *) &cli_addr, &sin_len); if(client_fd == -1) continue; memset(buffer, 0, BUFSIZE * sizeof(char)); read(client_fd, buffer, BUFSIZE - 1); if((strstr(buffer, "file.db")) != NULL) { memset(in_data, 0, 256 * sizeof(char)); memset(bufRec, 0, BSIZ * sizeof(char)); int i = 0; int ip = 0; for(i = 0; i < len_dat; i++) { memset(gp_file, 0, 32 * sizeof(char)); snprintf(gp_file, 29, "%s%d%s", "/sys/class/gpio/gpio", i, "/value"); if(ip > 54) { in_data = "0"; ip++; in_data = " "; ip++; } else { mf = fopen (gp_file, "r"); if(mf == NULL) error_log("gpio, fail last argument."); in_data = getc(mf); ip++; in_data = " "; ip++; fclose (mf); } } printf("Data:%s\n", in_data); int len_ara = (int)strlen(in_data) + 59; snprintf(bufRec, len_ara, "%s%s", response_text, in_data); write(client_fd, bufRec, len_ara - 1); close(client_fd); } else if((strstr(buffer, "comanda")) != NULL) /////////// comand { memset(To_GPIO, 0, 64); snprintf(To_GPIO, (int)strlen(patch_to_dir) + (int)strlen("init.sh 0 ") + 4, "%sinit.sh 0 %c%c%c", patch_to_dir, buffer, buffer, buffer); system(To_GPIO); close(client_fd); warning_access_log(buffer); printf("To Gpio:%s\n", To_GPIO); } else if((strstr(buffer, "GET / ")) != NULL) { read_in_file("index.html"); int len_ara = count_simvol + (int)strlen(response) + 1; memset(send2_array, 0, len_ara * sizeof(char)); snprintf(send2_array, len_ara, "%s%s", response, send1_array); write(client_fd, send2_array, count_simvol + 59); close(client_fd); warning_access_log(buffer); printf("Trans index.html.\n\n"); } else if((strstr(buffer, "style.css")) != NULL) { read_in_file("style.css"); int len_ara = count_simvol + (int)strlen(response_css) + 1; memset(send2_array, 0, len_ara * sizeof(char)); snprintf(send2_array, len_ara, "%s%s", response_css, send1_array); write(client_fd, send2_array, count_simvol + 58); close(client_fd); warning_access_log(buffer); printf("Trans style.css.\n\n"); } else if((strstr(buffer, "jquery.js")) != NULL) { read_in_file("jquery.js"); int len_ara = count_simvol + (int)strlen(response_js) + 1; memset(send2_array, 0, len_ara * sizeof(char)); snprintf(send2_array, len_ara, "%s%s", response_js, send1_array); write(client_fd, send2_array, count_simvol + 57); close(client_fd); warning_access_log(buffer); printf("Trans jquery.js.\n\n"); } else { write(client_fd, response_403, sizeof(response_403) - 1); close(client_fd); warning_access_log(buffer); } } } //END main //gcc -Wall -Wextra -Werror homestdrp.c -o homestdrp

Теперь копируем папку mydomrp в любое удобное место на RaspberryPi, например в корень, - /mydomrp , делаем файлы homestdrp и init.sh исполняемым…

Sudo chmod +x /mydomrp/homestdrp sudo chmod +x /mydomrp/init.sh

И заспускаем программу с тремя параметрами:

Sudo /mydomrp/homestdrp 80 /mydomrp/ 6

Позже, для автоматизации запуска, добавите эту команду в файл rc.local

Nano /etc/rc.local

Вписать надо до строчки exit 0 , вот так:

... (/mydomrp/homestdrp 80 /mydomrp/ 6)& exit 0

О параметрах:

Первый параметр - TCP порт. Порт можно указать любой, однако если у Вас больше нет никаких серверов занимающих стандартный (80) порт, то укажите его, ну в ежели занят, то напишите что-нибудь другое, например 82 (заходить в «умный дом» будете так - адрес:82 ) .

Второй параметр - путь к папке /mydomrp/ (с обязательным слешом / в конце) .

Четвёртый параметр - количество данных посылаемых клиену (вдаваться в подробности этого пункта не стоит, по крайней мере сейчас) , подсмотреть эту цифру нужно в файле index.html в строке if(vars.length == 6) . В коде она находится вот здесь:

Show(); setInterval(show,680); function show(){ if(flagobnov == 1) { $.ajax({ type: "POST", url: "file.db", timeout:560, cache: false, success: function(data){ var vars = data.split(" "); if(vars.length == 6) ЭТА ЦИфРА { count_obnov++; ...

После успешного старта, homestdrp первым делом запускает скрипт init.sh (Start init.sh) , который вначале удаляет все ссылки на все GPIO, а потом создаёт нужные (Export selected pin) и задаёт им режимы (in/out) (Initialization selected pin) .


На ошибки - sh: echo: I/O error не обращайте внимания, так и должно быть. Ведь скрипт очищает GPIO которых и так нет.

Теперь открыв страничку в браузере, вы увидите в терминале различные сообщения и отправку считанных данных GPIO:

Все действия homestdrp , сопровождаются записью в файл /var/log/Warning_AccessRp.log , и туда же пишутся предупреждения.

В случае критической ошибки (например не дописать аргумент) , она будет записана в файл /var/log/ErrorhomestdRp.log и программа остановится.

Теперь, если остановить homestdrp (Ctrl + c), то интерфейс сообщит об ошибке:

Пояснения

Как пользоваться кнопкой SENTSIG x?

Открыв файл init.sh (из папки /mydomrp) , среди прочего, вы найдёте вот такие строки:

104) # reaction to the button SENTSIG1 ;; 109) # reaction to the button SENTSIG2 ;; ...

Вот сюда то (вместо # reaction to the button SENTSIG1) и нужно вписывать свои команды.
То есть, если вместо "# reaction to the button SENTSIG1" написать reboot (без #) , сохранить файл и нажать в интерфейсе кнопку SENTSIG1 , то малинка перезагрузится, или если вместо "# reaction to the button SENTSIG2" написать apt-get update && apt-get upgrade , то обновится.

В общем можно делать всё, что душе угодно.

Пояснения к файлу index.html

Для наглядности откройте файл сформированный конструктором.

SetInterval(show,680); ...

… получает ответ в текстовом виде (данные разделены пробелами) и раскладывает их по переменным.

... /* приём */ if(vars == 1) { $(".d2otkl").show(); $(".d2vkl").hide(); } else if(vars == 0) { $(".d2otkl").hide(); $(".d2vkl").show(); } $("#indata3").html("INDATA3" + " " + vars); if(vars == 1) { $(".d3otkl").show(); $(".d3vkl").hide(); } else if(vars == 0) { $(".d3otkl").hide(); $(".d3vkl").show(); } ...

Если Вы устанавливаете систему там, где качество связи оставляет желать лучшего (например на даче) , то есть пинги туда очень большие, то будут появлятся ошибки «timeout». Во избежание этого, нужно увеличить таймаут запроса:

Show(); setInterval(show,680); function show(){ if(flagobnov == 1) { $.ajax({ type: "POST", url: "file.db", timeout:560, /* эта цифра (в миллисекундах)*/ cache: false, ...

По умолчанию стоит 560мс, увеличивайте её с шагом в 100 мс и пробуйте. Соответственно нужно увеличивать и setInterval(show,680) , так же на 100 мс.

Изменять названия кнопок (D2, D3, SENTSIG1 и т.д.) можно здесь:

...

D2
D2
SENTSIG1
...

Изменять названия полей для приёма данных (IN_BCM3, IN_BCM5 и т.д.) можно здесь:

... $("#indata3").html("IN_BCM3" + " " + vars); ...

Браузер постоянно запрашивает данные и тем самым создаёт трафик. Чтобы этого избежать, можно либо закрыть страницу, либо раскомментировать этот блок:

/*slmode++; if(slmode > 70) { $(".pansl").show(300); flagobnov = 0; slmode = 0; }*/

Тогда через ~минуту, страница будет закрываться полупрозрачной панелью и обновления остановятся. Клик на панель, уберёт её и обновления возобновяться.

После внесения изменений в index.html обязательно обновите страничку в браузере .

Все вопросы и пожелания пишите в комментах!

На этом пока всё, в

В данном проекте запускаем своё iOS, Android или Web приложение, а также пишем (вернее, чуть дописываем) чат бот на питоне, который управляет розетками через радио модуль, подключенный к Raspberry Pi.

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

Интересно?

А зачем?

Вопрос “а зачем это нужно?” не всегда остро стоит для проектов из серии «умный дом» и всяческих Internet of Things, здесь часто тратится куча времени и денег на автоматизацию чего-то, что на практике удобнее переключать по старинке обычным настенным выключателем:-) Но при этом получаешь кучу удовольствия и полезного опыта в процессе и далее радуешься исправной работе механизма, который сделал сам.

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

  • Человеческий язык - довольно легко заставить чат бот общаться с вами на человеческом языке, что довольно удобно, особенно для менее технических членов семьи. Также это позволяет в перспективе подключить голосовое управление.
  • Универсальный удаленный доступ - к XMPP чат серверу можно подключаться откуда угодно и из любого Jabber совместимого IM клиента. Людям удобно, а как подключить устройства описано в данной статье.
  • Удобное управление для всей семьи - подключите домашних в единый групповой чат, где сидят боты, отвечающие за дом или приборы в доме. Вы можете обсуждать свои семейные вопросы и одновременно управлять домом. Все видят, кто какие команды подал приборам, можно видеть их (ботов) ответы и отчеты, просмотреть предыдущую историю и т.п. Это удобно.
  • Система независимых распределенных агентов . Каждый прибор или набор датчиков, которые имеют представительство (по сути, пользователя) в чате, соответственно являются независимыми агентами и могут общаться с другими приборами и быть управляемыми ими или вами напрямую. Единый сервер не обязателен. Можно поднять свой чат бот для каждого важного прибора или датчика, а также настроить главного бота с элементами AI, такой себе “дворецкий” или “majordomo”, который будет всеми девайсами управлять, причем они будут общаться в доступном вам и другим членам семьи чате на понятном вам языке, так что вы всегда можете отследить что происходило или вмешаться в процесс.

Реквизит

Для данного мини-проекта нам потребуются следующие компоненты:

1. Raspberri P.
У меня модель B, заказал буквально за день до объявления о выходе B+, но в принципе, любая модель тут подойдет, главное смотрите, чтоб GPIO пины были совместимы с управляющим модулем, который вы выберете. Об этом ниже.
Ну а так, главное требование - запустить чат бот на питоне.

2. Аксессуары к вашему Pi.
WiFi модуль, простые USB клавиатура и мышь, карточка SD памяти с дистрибутивом Raspbian, блок питания, по желанию - пластиковый корпус.
Это стандарт для “малинки”, но т.к. я покупал её впервые, специально под этот проект, то не знал, что WiFi и SD карта не входят в стандартный комплект, и пришлось дозаказывать, так что имейте в виду. Также для настройки вам потребуется монитор или телевизор с HDMI кабелем.

3. Управляющий модуль (RF transmitter) и розетки или другие приборы с приёмником (RF receiver).
Здесь нужно сказать, что я пошел по быстрому или ленивому пути, и заказал готовый RF модуль для Pi и набор радиоуправляемых розеток от Energenie . В комплекте идет готовый RF передатчик, который подключается на GPIO пины вашей малинки. Кому этот путь не по душе, есть альтернативы, в инете куча гайдов о том, как к существующим радиоуправляемым приборам подобрать код и управлять ими через простой дешевый китайский RF передатчик. В качестве альтернативы можно через Pi управлять приборами непосредственно прямым проводным подключением с GPIO, а также через WiFi и по другим каналам.

Вот фото моего Energenie комплекта:

4. Чат-клиент.
В данном туториале используется Q-municate , это мессенджер с открытым исходным кодом от нашей платформы QuickBlox, который можно скачать с github и забилдить под iOS, Android или запустить Web версию на десктоп и других платформах. Преимущество использования Q-municate в том, что вы можете кастомизировать интерфейс под себя и сделать своё собственное приложение, например, только для своей семьи.
Но это совершенно не обязательно. Вы можете использовать любой Jabber/XMPP совместимый клиент, например Adium.

Итак, начнем.

Устанавливаем дистрибутивы / dependencies для Raspbian

Логинимся на малинку и ставим следующие вещи под рутом:
apt-get install python-dev pip install sleekxmpp pip install dnspython pip install pyasn1 pyasn1-modules apt-get install python-rpi.gpio
Нам собственно нужен sleekxmpp, это базовый проект для чат бота, а остальное решает вопросы с различными зависимостями для этого проекта. Ну и плюс python-rpi.gpio позволит контролировать GPIO пины малинки из нашего питон скрипта.

Подключаем и проверяем модуль радиоуправления

Если вы используете другой модуль, не от Energenie, то эту часть вам придется исследовать самостоятельно.

При использовании же готового Pi-mote модуля всё просто и хорошо описано в официальной инструкции от производителя: energenie4u.co.uk/res/pdfs/ENER314%20UM.pdf

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

Итак, обучаем. Согласно инструкции, запускаем скрипт

Sudo python ENER002.py
вставляем розетки в розетки) и если лампочки на них не мигают, то переводим в режим обучения нажатием кнопки выключения в течение 5 секунд. Лампочки замигали, нажимаем “Enter” на клавиатуре чтобы подать сигнал из скрипта и видим быстрое мигание лампочки, это значит что обучение прошло успешно. Повторяем то же самое с остальными розетками. Один Pi-mote модуль может подавать 4 разных кода, т.е. управлять можно 4 разными наборами Energenie розеток, при этом никто не мешает использовать один код для нескольких розеток одновременно.

Поднимаем чат сервер

Нам нужен XMPP / Jabber совместимый чат сервер с возможностью создания MUC (группового чата или чат комнаты) в нём, чтобы подключить туда наш чат бот и человеков-пользователей.

В принципе, на Pi можно поднять свой чат сервер, например вот здесь http://box.matto.nl/raspberryjabberd.html описывается установка ejabberd на Raspberri Pi.

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

Шаги ниже описывают регистрацию и заодно создание пользователя для нашего чат бота и MUC комнаты для общения.

Дальше нам нужно взять и модифицировать это под свои нужды.
Если вы тоже используете Energenie для управляемых розеток и QuickBlox для чат сервера, то вы можете взять мой готовый скрипт здесь: https://github.com/QuickBlox/sample-powerbot-python-rpi .
Вам нужно будет только поменять credentials в начале скрипта, прописав туда свои ключи приложения и пользователя (того, что мы создали выше).

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

1. Добавлено авто-присоединение по приглашению в другие чат комнаты.

2. Подправлена совместимость с QuickBlox и Q-municate (мелочи типа формата названия чат комнат и т.п.)

3. Добавлен собственно парсинг команд для управления приборами - в нашем случае это «lamp on», «lamp off», «all on» и «all off» - и вызов функций switch_on / switch_off из питон модуля energenie, который уже отдает команды на плату радиопередатчика через GPIO.
Кто работает напрямую с GPIO, посмотрите в energenie.py как реализована работа с GPIO.

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

Как реализовать авто-присоединение - парсим станзы входящих XML сообщений, т.к. нам обязательно прийдет сообщение о том, что создан такой-то MUC чат, если данный пользователь был туда приглашен.

В нашем случае мы используем платформу QuickBlox и конкретное приложение Q-municate, в нём приглашение в новый групповой чат выглядит примерно так:
RECV: Taras Filatov created new chat<_id>53b78c0c535c12798d0050551234040,1258466,126535021404538064Yanus Poluektovich, Sergey Fedunets[email protected]1

Отслеживаем фразу “created new chat” в XMPP станзах, и если она встречается, то парсим оттуда xmpp_room_jid, это и будет id вновь созданной комнаты.
Дальше запускаем процесс с этим же скриптом
Имейте в виду, для того чтоб это работало, нужно сделать скрипт исполняемым:

Chmod +x powerbot.py
Код реализации представлен ниже:

If msg["mucnick"] != self.nick and "Create new chat" in msg["body"]: from bs4 import BeautifulSoup y = BeautifulSoup(str(msg)) roomToJoin = y.xmpp_room_jid.string print ("Got an invite to join room") botId = subprocess.Popen(, shell=True) print "spawned new bot ID=" print botId self.send_message(mto=msg["from"].bare, mbody="Thank you for your kind invitation, joining your new room now!", mtype="groupchat")

Приветствие + инструкции
Определяем кодовое слово, в данном случае “powerbot”, и выдаем в ответ приветствие и подсказку о том, как пользоваться / общаться с нашим ботом.
Проверка “if msg["from"] != self.nick” нужна чтобы бот не реагировал на сообщения от себя самого.

# # Reply to help request (any message containing "powerbot" in it) # if msg["mucnick"] != self.nick and "powerbot" in msg["body"]: reply_test_message = self.make_message(mto=msg["from"].bare, mbody="Powerbot is greeting you, %s! Usage: lamp to control socket 1, all to control all sockets. Example: "lamp on" switched socket 1 on." % msg["mucnick"], mtype="groupchat") self.copy_dialog_id(msg, reply_test_message) reply_test_message.send() print "Sent help text: " + str(reply_test_message)

Включение / выключение лампы и других приборов
Отслеживаем команду “lamp on” (включить лампу), если команда получена, то включаем розетку switch_on(lampSocket) и отчитываемся о выполнении.

# # Handle "lamp on" command # if msg["mucnick"] != self.nick and "lamp on" in msg["body"]: switch_on(lampSocket) confirmation_message = self.make_message(mto=msg["from"].bare, mbody="Lamp has been switched on, %s." % msg["mucnick"], mtype="groupchat") self.copy_dialog_id(msg, confirmation_message) confirmation_message.send() print "Lamp switched on, sent confirmation: " + str(confirmation_message)
Аналогичным образом реализованы “lamp off”, “all on” и “all off” (последние отвечают за включение или выключение всех управляемых розеток).

Запускаем чат бот
Из bash на малинке выполняем нехитрую команду:

Sudo python powerbot.py -d -r <адрес начальной MUC комнаты>
Sudo нужно для доступа к GPIO. Если вы использовали QuickBlox, то в качестве адреса MUC комнаты просто скопируйте JID адрес из таблички Chat Dialogs.

В результате на экране появятся логи аутентификации и обмена XMPP статусами с сервером:

Всё, бот готов и ждет ваших указаний в чат-комнате.

Кстати, вы могли обратить внимание, что функции реакции на команды продублированы одновременно в
def message(self, msg):
и
def muc_message(self, msg):
первый блок обрабатывает приватные сообщения 1:1, а второй - групповые.
То есть управлять ботом можно и в приватном чате, хотя на мой взгляд, это менее интересно.

Собираем чат клиент под iOS (варианты: Android, Web)

Как я писал выше, можно общаться с ботом через любой Jabber / XMPP - совместимый чат клиент.
Мы легких путей не ищем, поэтому собираем своё приложение - клиент для управления ботом ну и заодно для общения с домашними и друзьями.
Свой собственный мессенджер с ботом и групповыми чатами:-)

Однако, как вы увидите, мы здесь тоже идем по быстрому и ленивому пути и берем готовый open-source проект нашей собственно разработки,
который называется Q-municate.

1. Тянем с гита проект для соответствующей платформы:
iOS: bitbucket.org/quickblox/qmunicate-ios
Android: github.com/QuickBlox/q-municate-android
Web: github.com/QuickBlox/q-municate-web

2. Открываем в IDE.

3. Правим креденшелы - меняем стандартные константы с ключами приложения на скопированные из админки QuickBlox. В iOS это меняется в AppDelegate.m, в Android в Consts.java.

4. Компилируем, билдуем на девайс.
По желанию кастомизируем интерфейс и прочее, .

Демонстрация

Скрины:


свой билд Q-municate я назвал гордо «Q-Power»


находим бота в френдах или групповом чате


общаемся с ботом

Видео управления лампой через чат:

Спасибо за внимание, надеюсь пригодится, а еще лучше если данный «proof of concept» сподвигнет кого-то реализовать идею до конца, и микроволновка вдруг заговорит с тостером, а давать им всем команды будет жена голосом через вашу домашнюю Siri:-)

Благодаря широкому ассортименту дополнительных модулей миниатюрный компьютер Raspberry Pi наилучшим образом подходит для любителей сборки недорогих систем умного дома (Smart Home) своими руками.

В качестве операционной системы можно использовать Raspbian, основанную на ядре Linux, вместе с такими расширениями, как Pimatic. Еще проще собрать «умный дом» можно с помощью комплексных программно-аппаратных решений на «открытой платформе», например openHAB, Fhem, SHC (SmartHome Control) или wiButler.

Модули Smart Home для Raspberry Pi

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

Так как Raspberry Pi - это популярный продукт для любителей мастерить, в продаже имеется огромный выбор модулей для Smart Home. Мы покажем вам некоторые из самых интересных.

433 МГц - приемник и передатчик для Raspberry Pi

Частота 433 МГц часто используется в компонентах доступных систем Smart Home, например, переключателях и термостатах радиаторов отопления, которые можно найти в строительных магазинах.

Такие передатчики и приемники идеально подходят для установки в систему «умный дом», построенную на Raspberry Pi. Бандл из этих двух модулей можно легко приобрести примерно за 600 рублей.

Модуль камеры для Raspberry Pi


С подключенным модулем камеры Raspberry Pi можно использовать в качестве системы видеонаблюдения.

Камера совместима с операционной системой Raspbian, она способна записывать видео в разрешении Full HD и делать 5-мегапиксельные фотографии.

Этот модуль доступен как с инфракрасным фильтром, так и без него по цене от 2000 рублей.

Датчик движения для Raspberry Pi

Если вы хотите, чтобы лампы освещения и другие электронные устройства (например, камера) включалось при появлении движения в какой-то области вашего дома, понадобится датчик движения, подключенный к системе умного дома.

Особенно привлекательным по цене является упаковка из пяти «пироэлектрических инфракрасных PIR датчиков движения».

Этот пакет вы можете приобрести всего за 480 рублей.

Датчик влажности и температуры воздуха для Raspberry Pi

Функционал метеостанции относится к базовому для Smart Home. Получать и обрабатывать метеоданные с помощью Raspberry Pi очень легко. Вам понадобится всего лишь один дешевый датчик, который вы подключите к мини-компьютеру: идеально подойдет DHT11, который стоит менее чем 600 рублей.

Модуль Enocean для Rapsberry Pi

Enocean - это беспроводная технология, которая обходится без источника питания. Суть вот в чем: энергия, необходимая для совершения того или иного действия, возникает из-за изменения состояния (нажатие на кнопку, разница температур, появление солнечного света, дуновение ветра и т. д.).

Соответственно, часто сопутствующими модулями являются переключатели или датчики температуры.

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

Пожарная сигнализация для Raspberry Pi

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

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

Модуль Homematic для Rapsberry Pi

Homematic является одной из самых популярных систем Smart Home в Европе. Для взаимодействия всех ее компонент, как правило, необходим центральный модуль управления CCU2 (MATIC Home Gateway).

Теперь вы можете соединить соответствующий модуль беспроводной связи с Raspberry. Один из таких, от компании ELV, стоит около 1700 рублей.

С представленными в этой статье модулями вы сможете построить весьма многофункциональную систему Smart Home. Однако, для Rapsberry Pi существуют еще множество других модулей, например, для работы с беспроводными стандартами Z-Wave и Zigbee.

Фото: компании-производители, CHIP.de

Введение

Данный проект берет свое начало в 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 для голосового управления. Но процесс реализации тянет на отдельную статью. Если сообществу будет интересна данная тема, то готов в ближайшее время подготовить еще одну статью.