Wi-Fi из PHP: сканирование, подключение, хотспот и watchdog для Raspberry Pi без монитора и клавиатуры

Если у вас есть что-то на PHP, что живёт на Raspberry Pi или в коробке под столом и должно само подключаться к Wi-Fi, без человека с клавиатурой, то эта библиотека как раз про это. Называется php-wifi. Ей семь лет, но версия 3.2 первая, которую я не побоюсь оставить работать где-нибудь далеко, без присмотра.
Писал я её из упрямства, хотелось, чтобы PHP тоже так умел. Внутри никакой магии, обычные системные утилиты nmcli, wpa_cli и hostapd. Так устроены многие PHP-библиотеки, тот же php-ffmpeg или gitonomy/gitlib. Утилита даёт стабильный интерфейс, библиотека добавляет типы, исключения и тесты.
composer require sanchescom/php-wifi
Что умеет
Четыре вещи, все через один объект.
Сканировать. Какие сети вокруг, сигнал, канал, диапазон, тип защиты. На выходе объекты, парсить вывод nmcli самому не надо.
use Sanchescom\WiFi\WiFi;
$wifi = WiFi::create(); // сам выбирает бэкенд под ОС
$strongest = $wifi->scan()->strongest();
printf("%s: %.0f dBm, %s\n", $strongest->ssid, $strongest->signal->dbm, $strongest->security->value);
scan() возвращает обычную Collection из illuminate/collections, к ней добавлено несколько своих методов: ->band(Band::GHz5), ->strongerThan(-70), ->uniqueBySsid(), ->connected(). А filter(), map(), first() работают как везде.
Подключаться и отключаться.
use Sanchescom\WiFi\Value\Credentials;
$wifi->connect($wifi->scan()->bySsid('Home'), Credentials::password('secret123'));
$wifi->disconnect();
Если в SSID опечатка, прилетит NetworkNotFound. Расшифровывать код возврата nmcli не придётся. Когда SSID известен заранее и сканировать незачем, есть connectTo('Home', …). Сохранённые сети можно посмотреть и удалить: knownNetworks(), forget().
Поднимать хотспот. Только Linux, зато с обоими бэкендами.
use Sanchescom\WiFi\Value\HotspotConfig;
$hotspot = $wifi->startHotspot(new HotspotConfig(ssid: 'setup', password: 'change-me-now'));
// ... позже
$wifi->stopHotspot();
Держать устройство на связи. Ради этого всё и затевалось. Pi стоит где-нибудь в углу, монитора и клавиатуры у него нет, и в три часа ночи он теряет Wi-Fi. Зайти по SSH уже нельзя, ввести пароль некому. wifi watch крутится сервисом и раз в несколько секунд проверяет соединение. Если оно пропало, переподключается. Если переподключиться не вышло, поднимает хотспот, чтобы устройство можно было настроить с телефона. Пока к хотспоту кто-то подключён, watchdog его не трогает. Когда все отключились и прошло --retry секунд, он гасит хотспот и снова пробует домашнюю сеть.
$ sudo vendor/bin/wifi watch --ssid=Home --hotspot-ssid=setup \
--hotspot-password-file=/etc/wifi-setup.pass --interval=5 --retry=20
2026-09-14T09:14:08+00:00 watch: hotspot_raised
2026-09-14T09:14:13+00:00 watch: hotspot_busy
2026-09-14T09:14:40+00:00 watch: recovered
2026-09-14T09:14:45+00:00 watch: connected
Лог настоящий, с моего Pi. --retry здесь короткий, чтобы всё влезло на экран. По умолчанию интервал 30 секунд, повтор через 300. Юнит для systemd лежит в examples/watch/. В examples/provision/ есть и страница настройки. Pi поднимает хотспот, телефон подключается к нему, выбирает домашнюю сеть из списка и вводит пароль.
CLI
Всё то же самое есть в виде команд, поверх того же API, с --json для скриптов:
$ vendor/bin/wifi list --unique
SSID BSSID Channel Band Quality dBm Frequency Connected Security
-------------------------------------------------------------------------------------------------
BELL340 0e:ac:8a:99:58:5c 1 2.4 100% -50 2412 false WPA2
VTECH_5764_9764 a6:97:5c:b7:97:64 1 2.4 65% -68 2412 false WPA2
- 0e:ac:8a:99:58:5e 157 5 75% -62 5785 false WPA2
wifi device, wifi connect, wifi disconnect, wifi known, wifi forget, wifi hotspot start|stop|status, wifi watch. Пароль можно передать файлом или через stdin (--password-file=-), тогда в командной строке и в ps его нет.
Где работает
В основном это библиотека для Linux. Бэкенда два, нужный выбирается сам. Если запущен NetworkManager, всё идёт через nmcli. Если нет, через wpa_cli к wpa_supplicant, а хотспот тогда поднимают hostapd и dnsmasq. Второй случай встречается на Raspberry Pi OS Lite и вообще на любом минимальном образе, где NetworkManager не стоит.
Сразу про права, потому что об них спотыкаются первым делом. Сканировать на Debian можно без root, если пользователь в группе netdev. Подключение и хотспот меняют состояние сети, так что либо sudo, либо правило polkit, готовое есть в README. Хотспот на hostapd в любом случае под root: он поднимает интерфейс и запускает dnsmasq.
Всё, что в таблице помечено «проверено», я гонял на своём Pi 4 (Debian 13) перед тем, как ставить тег на релиз. Логи этих прогонов лежат в репозитории, в docs/verified-on.md, вместе с тем, что не заработало.
Linux | Linux | macOS | Windows | |
|---|---|---|---|---|
сканирование | проверено | проверено | проверено, SSID скрыты без доступа к геолокации | только тесты |
подключение / отключение | проверено | проверено, включая DHCP | только тесты | только тесты |
сохранённые сети | проверено |
| не поддерживается | не поддерживается |
хотспот | проверено | проверено | не поддерживается | не поддерживается |
watchdog | только тесты | проверено, включая настоящий телефон на хотспоте | не поддерживается | не поддерживается |
macOS и Windows нужны, чтобы тот же код запускался на ноутбуке разработчика: сканирование и подключение там работают, остальное бросает UnsupportedOperation. Можно проверить заранее: $wifi->supports(SupportsHotspot::class). Про Windows честно скажу сразу. Первую версию я проверял на ней в 2019 году, и она работала, а третью на живой Windows не запускал ни разу, бэкенд на netsh покрыт только тестами. Если у вас есть Windows под рукой, отчёт о прогоне будет очень кстати.
Два момента, про которые лучше знать заранее
Пароль не светится в argv. nmcli получает его через stdin (--ask), wpa_cli тоже через stdin, скриптом, hostapd читает из конфига с правами 0600, который удаляется, как только демон его прочитал. Я это проверял. Во время живого connect на Pi снимал ps -ww -eo args и искал в нём пароль, причём шаблон для grep читался из файла, чтобы сам grep пароль не нёс. Ноль совпадений. Одно исключение осталось. У NetworkManager команда запуска хотспота принимает пароль только аргументом. Лечится через keyfile, это в планах.
Утилиты ищутся по абсолютному пути. На Debian у непривилегированного пользователя в PATH нет /usr/sbin, а именно там лежат wpa_cli, iw и dnsmasq. При этом proc_open() в PHP ищет программу по PATH родительского процесса, и тот PATH, который вы передаёте в окружении команды, на поиск не влияет. Поэтому библиотека сначала находит каждую утилиту через which по системным каталогам. Если у вас когда-нибудь PHP-скрипт не мог запустить утилиту, которая точно стояла, скорее всего, дело было в этом.
Чего не делает
Веб-панели нет и не планируется. Постоянный веб-интерфейс с правом менять Wi-Fi работает под root и становится главной дырой на устройстве, а кому нужна панель, у того есть RaspAP. Нет хотспота с WPA3. Нет настройки IPv6. На macOS чужие SSID и BSSID видны только подписанному хелперу на CoreWLAN, такого хелпера в библиотеке нет, она отдаёт то, что даёт system_profiler, и помечает имена как скрытые. Полный список с причинами в roadmap.
Что дальше
В 3.3 появится wifi provision. Это одна команда, после которой устройство настраивается с телефона, как умная колонка. Устройство показывает QR-код своего хотспота, телефон подключается и сам открывает страницу настройки (captive portal), страница показывает список сетей и человеческим языком объясняет, почему подключиться не вышло, а устройство после этого доступно как <имя>.local. Большая часть уже есть в examples/provision/, осталось собрать это в команду.
Код, документация и логи с железа: github.com/sanchescom/php-wifi.
KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.