Привет.
Сегодня в большом числе проектов домашней (и не только) автоматизации используется Raspberry Pi. При этом достаточно удобно иметь не только прямой доступ к устройству, но и использовать браузер — это позволяет выполнять необходимые действия и с компьютера, и с мобильного телефона, и даже удаленно из любой точки мира.
Допустим, у нас уже есть супер Python-программа, делающая что-то очень важное, от мигания светодиодом до управления «умным домом» или хотя бы кормушкой для кота. Я покажу разные способы, от простого к сложному, как сделать web-доступ к такому приложению, добавив немного кода.
Для тех кому интересно, продолжение под катом.
Примечание: эта статья является своего рода «экспериментом», как-то в комментариях жаловались что на Хабре недостаточно статей для начинающих. Я попытался восполнить пробел, ну а по оценкам будет видно, имеет смысл продолжать в таком формате или нет. Профи вряд ли найдут здесь что-то кардинально новое, ну а новичкам в Linux надеюсь, будет полезно.
Итак, приступим.
Настройка Raspberry Pi
Будем надеятся, что у читателя есть Raspberry Pi, которая подключена к домашней сети через WiFi или Ethernet, и читатель знает что такое IP адрес и как зайти удаленно на Raspberry Pi через SSH при помощи putty. Мы будем рассматривать так называемую headless-конфигурацию — без клавиатуры и монитора. Но перед тем, как делать что-то с Raspberry Pi, пара небольших лайфхаков.
Совет N1. Чтобы что-то удаленно делать с Raspberry Pi, на нем нужно настроить SSH, а по умолчанию он выключен. Можно пойти традиционным способом, и запустить стандартный конфигуратор, но можно сделать проще — после записи образа диска достаточно создать пустой файл ssh (без расширения) в корне SD-карты. Дальше, после загрузки Raspberry Pi, SSH будет сразу активен.
Чтобы зайти удаленно на устройство, нужно узнать IP-адрес Raspberry Pi. Для этого достаточно открыть контрольную панель своего маршрутизатора, найти там список DHCP-клиентов, скопировать оттуда нужный IP-адрес (например, это будет 192.168.1.102), и ввести команду putty.exe pi@192.168.1.102 (для Windows) или ssh pi@192.168.1.102 для Linux или OSX.
Однако, IP-адреса могут меняться, например после перезагрузки маршрутизатора, это не всегда удобно. Из этого следует Совет N2 — настроить статический IP-адрес. Для этого на Raspberry Pi выполняем команду sudo nano /etc/dhcpcd.conf, и вводим следующие настройки:
interface eth0
static ip_address=192.168.1.152/24
static routers=192.168.1.1
static domain_name_servers=192.168.1.1 8.8.8.8
Если нужен адрес WiFi, то интерфейс будет wlan0, если Ethernet то eth0. IP-адреса разумеется, нужно тоже подставить свои. После перезагрузки убеждаемся что IP-адрес правильный, введя команду ifconfig.
Теперь все готово, можем приступать к Python. Все примеры даны для Python 3.7, т.к 2.7 уже давно устарел, и поддерживать его бесмысленно. Но при небольших изменениях кода все заработает и там, если нужно. Кстати, язык Python является кроссплатформенным — это значит что весь приведенный ниже код можно запустить и на Windows и на OSX, ну и разумеется, на Raspberry Pi. Из этого следует Совет N3 — отлаживать программу можно и на обычном ПК, а уже готовую версию заливать на Raspberry Pi. Возможно, придется лишь сделать функции-обертки для методов GPIO, все остальное будет работать.
Итак, наша задача — обеспечить доступ к приложению через обычный браузер. Ибо это стильно-модно-молодежно, ну и «интернет вещей» это наше все.
Способ 1: командная строка
Самый простой способ, не требующий вообще никакого программирования.
Выбираем нужную папку на Raspberry Pi, и вводим команду:
python3 -m http.server 5000
Все, на Raspberry Pi работает файловый сервер! Достаточно зайти на страницу http://192.168.1.102:5000 и мы увидим наши файлы в браузере:
Это достаточно удобно, если нужно открыть удаленный доступ к каким-либо файлам с минимумом затраченных сил. Можно также ввести команду sudo python3 -m http.server 80 и запустить сервер со стандартным 80-м портом, это позволит не указывать порт в адресной строке браузера.
Кстати, если мы хотим, чтобы сервер работал и после закрытия терминала, можно использовать команду sudo nohup python3 -m http.server 80 & — это запустит процесс в фоне. Убить такую программу можно перезагрузкой, или вводом в командной строке команды sudo killall python3.
Способ 2: SimpleHTTPServer
Мы можем довольно просто интегрировать такой же сервер в нашу программу на Python, для этого достаточно запустить его отдельным потоком при старте программы. Теперь, нам не надо возиться с командной строкой, пока программа запущена, сервер будет работать.
import http.server
import socketserver
from threading import Thread
import os
def server_thread(port):
handler = http.server.SimpleHTTPRequestHandler
with socketserver.TCPServer(("", port), handler) as httpd:
httpd.serve_forever()
if __name__ == '__main__':
port = 8000
print("Starting server at port %d" % port)
os.chdir("/home/pi/Documents")
Thread(target=server_thread, args=(port,)).start()
Команда os.chdir является опциональной, если мы хотим предоставить доступ из сервера к какой-то другой папке, кроме текущей.
Способ 3: HTTPServer
Это уже полноценный web-сервер, способный обрабатывать GET и POST-запросы, возвращать разные данные и пр. Но и кода разумеется, понадобится больше.
Рассмотрим минимально работающий вариант сервера:
from http.server import BaseHTTPRequestHandler, HTTPServer
html = "<html><body>Hello from the Raspberry Pi</body></html>"
class ServerHandler(BaseHTTPRequestHandler):
def do_GET(self):
if self.path == "/":
self.send_response(200)
self.send_header('Content-type', 'text/html')
self.end_headers()
self.wfile.write(html.encode('utf-8'))
else:
self.send_error(404, "Page Not Found {}".format(self.path))
def server_thread(port):
server_address = ('', port)
httpd = HTTPServer(server_address, ServerHandler)
try:
httpd.serve_forever()
except KeyboardInterrupt:
pass
httpd.server_close()
if __name__ == '__main__':
port = 8000
print("Starting server at port %d" % port)
server_thread(port)
Запускаем браузер, и видим в нем нашу HTML-страницу:
Данный сервер несложно научить отдавать файлы, например изображения.
Добавим в HTML тег img:
html = '<html><body><h3>Hello from the Raspberry Pi</h3><img src="raspberrypi.jpg"/></body></html>'
Исходный файл «raspberrypi.jpg» разумеется, должен лежать в папке с программой. Добавим в функцию do_GET возможность получения файлов:
def do_GET(self):
print("GET request, Path:", self.path)
if self.path == "/":
self.send_response(200)
self.send_header('Content-type', 'text/html')
self.end_headers()
self.wfile.write(html.encode('utf-8'))
elif self.path.endswith(".jpg"):
self.send_response(200)
self.send_header('Content-type', 'image/jpg')
self.end_headers()
with open(os.curdir + os.sep + self.path, 'rb') as file:
self.wfile.write(file.read())
else:
self.send_error(404, "Page Not Found {}".format(self.path))
Запускаем сервер, и видим соответствующую картинку:
Вряд ли такой сервер выиграет конкурс веб-дизайна, но он вполне работает. Сервер несложно заставить отдавать более полезные данные, например возвращать информацию о работе программы. Для примера добавим обработчик для новой функции status:
import psutil
import json
def cpu_temperature():
return psutil.sensors_temperatures()['cpu-thermal'][0].current
def disk_space():
st = psutil.disk_usage(".")
return st.free, st.total
def cpu_load() -> int:
return int(psutil.cpu_percent())
def ram_usage() -> int:
return int(psutil.virtual_memory().percent)
def do_GET(self):
...
elif self.path == "/status":
self.send_response(200)
self.send_header('Content-Type', 'application/json')
self.end_headers()
health = {'CPUTemp': cpu_temperature(), 'CPULoad': cpu_load(), "DiskFree": disk_space()[0], "DiskTotal": disk_space()[1], "RAMUse": ram_usage()}
self.wfile.write(json.dumps(health).encode('utf-8'))
Теперь мы можем открыть в браузере ссылку http://192.168.1.102:5000/status и увидеть текущие параметры системы:
Кстати, как можно видеть, мы отдаем данные в формате JSON, что позволит использовать их для каких-то других запросов.
Заключение
Все задуманное в одну часть не влезло. Если продолжение будет аудитории интересно, в следующей части я расскажу об интерактивном сервере с Javascript и об использовании flask.
Важно: меры безопасности
Если для Raspberry Pi будет использоваться внешний IP-адрес, обязательно стоит помнить о мерах безопасности. Может показаться что ваш мини-сервер никому не нужен, однако сейчас не составляет труда пакетно просканировать все диапазоны IP-адресов (как пример, Украина, Австрия) и найти все доступные устройства. Так что обязательно стоит поменять пароль на Raspberry Pi, и не стоит хранить на устройстве какую-либо конфиденциальную информацию (папки Dropbox, имена/пароли захардкоженные в скриптах, фото и пр).
Автор: DmitrySpb79