USBIP проброс ZigBee шлюза и мониторинг состояния через Telegram

Сразу же немного погружу вас в термины, если вдруг они вам не знакомы

ZigBee — технология применяемая в системах умный дом. Подробнее можно почитать например на Zigbee Wiki

USB/IP — проект позволяющий подключать к компьютеру USB устройства удалённо. Подробнее о проекте на его официальной странице.

DD-WRT — свободная, основанная на Linux, альтернативная прошивка для маршрутизаторов.

Важное замечание: Для использованя USB/IP рекомендуется ширина канала 1Gb, в остальных случаях возможны проблемы.

Поскольку ZigBee устройства работают на частоте 2.4 ГГц, то для них также присущи проблемы, которые есть и у Wi-Fi, такие как забитые каналы и дальность. Поэтому иногда (как например в моей ситуации), необходимо разместить ZigBee шлюз в более оптимальном месте, чем то, которое занимает сервер «Умного дома». Конечно же есть выход обеспечить все вокруг ZigBee роутерами и тогда проблема по идее уйдёт. Но кто же ищет лёгких путей.

Итак, для проброса ZigBee шлюза нам потребуется сам шлюз в виде USB стика, я использую версию 3, вот ссылка на сайт автора: https://modkam.ru/?p=1112

Серверная часть

Роутер с установленной прошивкой DD-WRT. Узнать можно ли прошить ваш роутер можно на сайте проекта: https://dd-wrt.com/

Также необходимо, чтобы ваш роутер поддерживал USBIP (практически все более-менее новые роутеры с USB разёмами поддерживают) посмотреть это можно во вкладке Services — USB, должна быть возможность активации USB Over IP, если вдруг нет, то при помощи Wiki DD-WRT можно проверить руками. Ссылка: https://wiki.dd-wrt.com/wiki/index.php/USB

В общем с этим мы разобрались, на роутери активировали USB/IP теперь нам надо получить ID устройства, для этого при помощи SSH или Telnet подключаемся к нашему роутеру и выполняем следующую команду (USB устройство должно быть в гнезде конечно же):

# usbip list -l

Ответ должен быть примерно такой

 - busid 2-2 (0451:16c8)
   Texas Instruments, Inc. : unknown product (0451:16c8)

Получилось? Отлично! busid — id нашего устройства, его необходимо запомнить или записать. Двигаемся дальше. Теперь нам надо открыть доступ к устройству, делается это очень просто:

# usbip bind --busid 2-2

Если всё прошло успешно, то в консоли вы должны будите увидеть такого рода ответ:

bind device on busid 2-2: complete

Теперь в веб-интерфейсе роутера переходим в раздел Administration — Commands, вносим туда команду

usbip bind --busid 2-2

И нажимаем Save Startup. Собственно настройка сервера на этом завершена.

Клиентская часть

Далее переходим к клиенту на базе Linux (в Windows наверное тоже как-то можно сделать, но я не в курсе как). Подключаемся к устройству которое будет выступать клиентом через SSH и выполняем команду:

# usbip list --remote 192.168.0.1

Где 192.168.0.1 — ip вашего сервера. Вывод должен быть примерно такой:

Exportable USB devices
======================
 - 192.168.0.1
        2-2: Texas Instruments, Inc. : unknown product (0451:16c8)
           : /sys/devices/pci0000:00/0000:00:0b.0/usb2/2-2
           : Communications / unknown subclass / unknown protocol (02/00/00)
           :  0 - Communications / Abstract (modem) / AT-commands (v.25ter) (02/02/01)
           :  1 - CDC Data / Unused / unknown protocol (0a/00/00)

Отлично! Устройство мы видим, нужно его примонтировать, делается это командой :

# usbip attach --remote 192.168.0.1 --busid 2-2

Где 192.168.0.1 — ip сервера, а 2-2 — id устройства.

Узнать порт устройства можно выполнив команду:

# usbip port

Выведет порт USBIP, а команда:

# ls -l /dev/serial/by-id/

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

Вроде бы как и всё, но нет в USBIP куча подводных камней и прочих тягостей и лишений которые нам нужно преодолеть. Подробней о них в статье на Хабре: https://habr.com/ru/post/308860/

Прочитав статью мы поняли, что нужно будет написать скрипт для мониторинга состояния устройства, я сделал два варианта, на BASH и на Python. Вариант на BASH просто молча переподключает устройство и не проверяет подключён ли модуль ядра, а просто тупо его всегда подключает (лень переписывать), а вариант на Python ещё и отправляет уведомления в телеграм.

Вариант скрипта на BASH

Предварительно нужно установить sshpass:

# apt update && apt install -y sshpass
#!/bin/bash
HOST=192.168.0.1 #адрес хоста 
check=$(usbip list -r "$HOST")
device=$(lsusb | grep 0451:16c8)
# echo $check
if [ -z "$device" ]
then
  echo "No device"
  modprobe vhci-hcd
  usbip attach --remote 192.168.0.1 --busid 2-2
  if [ -z "$device" ]
  then
    sshpass -p 'пароль_root' ssh -q -o 'UserKnownHostsFile /dev/null' -o 'StrictHostKeyChecking no' root@192.168.0.1 'usbip unbind -b 2-2; usbip bind -b 2-2' # Из-за особенностей USBIP нужно отключить устройство на сервере и подключить обратно. Я, кстати, не рекомендую использовать такой способ подключения, лучше через ключи и конечно лучше не отключать проверку ключа хоста, но "Горм не грянет, мужик не перекрестится" 
    check=$(usbip list -r "$HOST")
    if [ -n "$check" ]
    then
      usbip attach --remote 192.168.0.1 --busid 2-2
      echo "device rebinded"
    else
      echo "Some troubles"
    fi
  else
    echo "Host is"
  fi
else
  echo "device attached"
fi
exit 0

Вариант скрипта на Python c блекджеком и телеграмом

Для начала нужно создать бота и получить токен, как это сделать описано много где, вот поисковая выдача

Далее нам нужно получить id чата с нашим ботом, для этого, предварительно что-нибудь написав боту, нужно в браузере открыть страницу

https://api.telegram.org/botТОКЕН/getUpdates

Оттуда скопировать id чата, это будет значение параметра id

Нужно также предварительно скачать библиотеки для Python 3.X, а для этого поставить pip3, в консоли выполняем:

# apt update && apt install -y python3-pip

Выполнение следующего шага необходимо если доступ к api телеграм заблокирован. В таком случае для соединения мы будем использовать SOCS5 Proxy. Поскольку у меня есть свой Proxy сервер за пределами России, я использую его. Но в случае если у вас нет возможности использовать, то можно воспользоваться бесплатными прокси. Либо сделать небольшой донат мне здесь и после этого сообщив на admin@vakorin.net информацию. Я предоставлю доступ. ВНИМАНИЕ: Я не гарантирую вам постоянную работоспособность сервиса!

# pip3 install requests[socks]

На этом подготовительные работы закончены, вот сам скрипт:

#!/usr/bin/python3
import subprocess as s
import requests


host = "192.168.0.1" # Ваш хост
id = "0451:16c8" #ID устройства можно узнать через lsusb
server_user = "root"
server_pass = "" # Пароль root роутера
usbip_id = "2-2"
proxies = {
    'https': 'socks5h://username:password@address:port' #Если по какой-то причине не сработает socks5h, попробуйте изменить на socks5
}


def send_telegram(text: str):
    token = "ТОКЕН БОТА"
    url = "https://api.telegram.org/bot"
    channel_id = "ID канала"
    url += token
    method = url + "/sendMessage"

    r = requests.post(method, data={
        "chat_id": channel_id,
        "text": text
    }, proxies=proxies)
    if r.status_code != 200:
        raise Exception("post_text error")


def cmd(cmd):
    data = s.run(cmd, shell=True, stdout=s.DEVNULL)
    reply = data.returncode
    return reply


def route():
    device = cmd("lsusb | grep " + id)
    if device == 1:
        modprobe = cmd("lsmod | grep vhci_hcd")
        if modprobe == 1:
            cmd("modprobe vhci-hcd")
        else:
            i = 0
            while i < 5:
                attach = cmd("usbip attach --remote " + host + " --busid " + usbip_id)
                if attach == 0:
                    send_telegram('Котёнок, ZigBee шлюз отвалился, но я всё починила, правда я '
                                  'милашка? :)')
                    break
                else:
                    cmd("sshpass -p '" + server_pass + "' ssh -q -o 'UserKnownHostsFile /dev/null' -o "
                                                       "'StrictHostKeyChecking no' " + server_user + "@" + host + " 'usbip unbind -b " + usbip_id + "; usbip "
                                                                                                                                                    "bind -b "
                                                                                                                                                    "" + usbip_id + "'")
                    i += 1
            if i > 4:
                send_telegram('Лапочка, тут такое дело.... \nЯ пробовала 4 раза и не смогла подцепить '
                              'ZigBee шлюз. Не мог бы ты посмотреть пожалуйста')
    else:
        pass


route()

На этом со скриптами всё, но кто же будет это всё постоянно запускать? Возникнет вопрос у читателя. Конечно же systemd

Юнит и таймер systemd

Я также не буду расписывать подробно, просто оставлю ссылку на один сайт, где всё более-менее разжевано. https://sys-adm.in/os/nix/718-primer-ispolzovaniya-systemd-timer-ili-kak-zabyt-pro-cron.html

А от себя выложу листинги

Юнит

[Unit]
 Description=Run usbip check
 Requires=network.target
 After=network.target
[Service]
  Type=simple
  ExecStart= /opt/usbip_check.py # место расположения скрипта

Таймер

[Unit]
Description = usbip check timer

[Timer]
OnBootSec=15sec
OnUnitActiveSec=20sec
Unit=usbip_check.service

[Install]
WantedBy=multi-user.target

Итоги

Зона охвата ZigBee шлюза возросла (если ротуер где-то в центре помещения конечно), systemd следит за тем, чтобы смонтированная флешка была на месте. А мы получаем в Telegram примерно такие сообщения.

Добавить комментарий

Этот сайт использует Akismet для борьбы со спамом. Узнайте как обрабатываются ваши данные комментариев.