Сразу же немного погружу вас в термины, если вдруг они вам не знакомы
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 примерно такие сообщения.
Котёнок, спасибо за статью)