Полное воспроизводимое руководство по сбору баланса, тарифа и доступных пакетных/финансовых полей мобильных номеров в Home Assistant. Рабочая схема вынесена из Home Assistant на отдельный Ubuntu-хост: MBPlugin получает данные операторов, сохраняет их в SQLite, а отдельный bridge публикует проверенные поля через MQTT Discovery.
Готовая команда для системного помощника находится в ASSISTANT_PROMPT.md. Она специально требует инвентаризацию, резервную копию, ручной ввод SMS/CAPTCHA владельцем и сквозную приёмку.
Проверенный стенд: Ubuntu, Docker Compose, MBPlugin
v1.00.92, системный Chromium, Playwright1.46.0, Mosquitto и Home Assistant MQTT Discovery. Последняя сквозная проверка этой реализации — 23 сентября 2026 года. Сайты операторов меняются, поэтому перед production нужно повторить все тесты.
Это независимый комплект поверх artyl/mbplugin. Модули p_beeline_sms, p_megafon_sms и p_mts_sms из этого репозитория не входят в чистый upstream MBPlugin. Одного добавления строк в phones.ini недостаточно: для воспроизводимости нужны приложенные Dockerfile, адаптеры, runtime, MQTT bridge и процедура сохранения сессии.
Комплект предназначен для владельца или уполномоченного администратора SIM. Он не обходит CAPTCHA, не перехватывает SMS и не вводит пароль. Владелец проходит защитные шаги сам в краткоживущем браузерном окне.
flowchart LR
U[Владелец] -->|SMS/CAPTCHA вручную| N[Временный noVNC 20 минут]
N --> S[(Приватные browser sessions)]
C[MBPlugin collector] --> S
C --> O[Кабинеты операторов]
C --> D[(SQLite)]
B[Read-only MQTT bridge] --> D
B -->|retained QoS 1 + LWT| M[Mosquitto]
M --> H[Home Assistant MQTT Discovery]
Старый Home Assistant add-on не используется. Home Assistant остаётся потребителем MQTT, а тяжёлая браузерная часть живёт в изолированном Docker Compose на Ubuntu.
runtime/ Docker image, collector, bridge, tests, backup
runtime/carrier/ p_beeline_sms, p_megafon_sms, p_mts_sms
runtime/Dockerfile загрузка закреплённого исходника MBPlugin v1.00.92
login-window/ временный noVNC для ручной авторизации
examples/ обезличенные phones.ini и bridge.json
ASSISTANT_PROMPT.md готовое задание помощнику
Dockerfile загружает архив точного commit 9e7d4de2359a4f5df6c193b372f9e13085b2659b (tag v1.00.92) с SHA-256 252a6dbad7cf04241547e70d0d039213d2ab9fc94825fd47073f9a2e12973bd2. BuildKit проверяет checksum до распаковки. Upstream test fixtures удаляются из runtime image. Условия upstream приведены в THIRD_PARTY_NOTICES.md.
- реальные номера, ФИО, договоры, балансы, тарифы и даты платежей;
phones.ini,bridge.json,.env, SQLite, raw payload и логи;- cookies, JWT, localStorage, browser profiles и state-файлы;
- SMS-коды, CAPTCHA, пароли MQTT/noVNC/HA и токены;
- IP, частные домены, VPN-профили, временные URL и tunnel keys;
- dashboard JSON/entity IDs, если они раскрывают номер или имя.
Каталог /data, login private/ и backup должны быть 0700; секретные файлы — 0600. Они уже исключены .gitignore, но перед каждой публикацией всё равно сканируйте рабочее дерево и историю Git.
- Ubuntu/Debian x86_64 с актуальными security updates;
- Docker Engine и Compose v2;
- минимум 4 GB свободного места, 2 GB RAM для collector;
- существующий Mosquitto или другой MQTT broker;
- MQTT integration и Discovery в Home Assistant;
- HTTPS/VPN/SSH-маршрут для временного noVNC, если владелец входит не с самого Ubuntu;
- возможность получить SMS хотя бы при первичном входе или подтверждённый контактный номер у оператора.
Перед началом зафиксируйте версии Docker/Compose, состояние HA/MQTT, существующие MBPlugin, topic prefix и entity IDs. Не перезапускайте весь Home Assistant, MQTT или хост без доказанной необходимости.
Выберите приватный каталог, например /srv/mbplugin. Не копируйте этот путь вслепую: проверьте диск, права и резервное хранилище.
sudo install -d -m 0700 -o 10001 -g 10001 /srv/mbplugin/data
sudo install -d -m 0700 /srv/mbplugin-backups
cp -a runtime /srv/mbplugin/app
cp -a login-window /srv/mbplugin/login-window
cd /srv/mbplugin/appЕсли MBPlugin уже существует, сначала сохраните image digest, Compose/INI и полный /data. Приложенный backup останавливает только collector, создаёт tar.gz, SHA-256 и проверяет читаемость:
sudo ./backup.sh /srv/mbplugin-backupsНе считайте backup пригодным, пока не прошли sha256sum -c и tar -tzf. Не создавайте архив внутри копируемого каталога: одна из реальных попыток дала гонку, когда проверочный файл попал в tar во время упаковки.
cd /srv/mbplugin/app
MBPLUGIN_DATA=/srv/mbplugin/data docker compose build --pull mbplugin
MBPLUGIN_DATA=/srv/mbplugin/data docker compose config >/dev/nullКонтейнер работает как UID/GID 10001, с read-only rootfs, dropped capabilities, no-new-privileges, полным seccomp-профилем и loopback-портом 127.0.0.1:19777. Docker socket, privileged и host network не нужны.
В upstream Playwright 1.46.0 использовал удалённый Chromium-флаг --headless=old; hardening заменяет его на --headless=new, включает Chromium sandbox, TLS verification, отключает изменяющие HTTP endpoints и закрепляет data root. Если сборка перестала находить точку patch, остановитесь: upstream изменился, и слепое продолжение небезопасно.
sudo chown -R 10001:10001 /srv/mbplugin/data
sudo chmod 0700 /srv/mbplugin/data
MBPLUGIN_DATA=/srv/mbplugin/data docker compose up -d mbplugin
docker compose ps
docker compose exec mbplugin python /data/mbplugin/plugin/util.py -v version
docker compose exec mbplugin python /data/mbplugin/plugin/util.py check-import
docker compose exec mbplugin python /data/mbplugin/plugin/util.py check-ini
docker compose exec mbplugin python /data/mbplugin/plugin/util.py check-playwrightPOLL_ENABLED=0 оставляют до подключения аккаунтов. HTTP-отчёт доступен только с хоста или через SSH tunnel:
ssh -L 19777:127.0.0.1:19777 <ubuntu-host>Проверьте, что /report читается, а POST, query-команды и изменяющие endpoints возвращают 403.
Скопируйте структуру из examples/phones.example.ini в приватный /srv/mbplugin/data/phones.ini. Number — десять цифр без +7/8. Каждый связанный номер получает отдельную секцию, но использует общую сохранённую сессию своего оператора.
[Phone] #1
Region = p_beeline_sms
Monitor = TRUE
Alias = mobile_beeline_1
Number = <TEN_DIGIT_NUMBER>
Password2 = session-only-never-usedPassword2 здесь — служебная непустая строка для формата MBPlugin; адаптеры не используют её для входа. Не помещайте реальный пароль в файл.
sudo chown 10001:10001 /srv/mbplugin/data/phones.ini
sudo chmod 0600 /srv/mbplugin/data/phones.ini
docker compose exec mbplugin python /data/mbplugin/plugin/util.py check-iniОстановите collector, чтобы два Chromium не открыли один профиль, и сделайте backup. Затем создайте пароль Basic Auth без вывода его в историю/чат:
cd /srv/mbplugin/app/../login-window
install -d -m 0700 private
htpasswd -B -c private/login.htpasswd operator
sudo chown -R 10001:10001 private
chmod 0600 private/login.htpasswdСкопируйте login-window рядом с runtime либо скорректируйте относительный seccomp path. Запустите один оператор:
docker compose -f /srv/mbplugin/app/compose.yaml stop mbplugin
cd /srv/mbplugin/login-window
OPERATOR=beeline MBPLUGIN_DATA=/srv/mbplugin/data docker compose up -d --buildОкно на хосте:
http://127.0.0.1:16080/vnc.html?autoconnect=true&resize=scale
Для другого компьютера используйте SSH tunnel. Для телефона создайте отдельный краткоживущий HTTPS route с аутентификацией к 127.0.0.1:16080, не публикуя backend напрямую. Проверьте 401 без Basic Auth и WebSocket 101 после входа. Route должен иметь auto-close не более 20 минут.
Владелец сам вводит номер, CAPTCHA и SMS. После появления кабинета сохраните сессию, пока окно ещё работает:
OPERATOR=beeline MBPLUGIN_DATA=/srv/mbplugin/data \
docker compose exec -e OPERATOR=beeline login python /app/save-session.pyОжидаемый ответ: session_saved. Затем остановите окно, удалите временный внешний route и снаружи убедитесь, что URL больше не открывается:
OPERATOR=beeline MBPLUGIN_DATA=/srv/mbplugin/data docker compose down
docker compose -f /srv/mbplugin/app/compose.yaml start mbpluginПовторите для megafon и mts. Не используйте одновременно два login-window для одного /data.
- Для постоплаты API-поле
balance.balanceоказалось текущими расходами, а баланс договора — минусdebt.balance. Адаптер публикует их раздельно вместе с лимитом и доступным кредитом. - Связанные мобильные номера читаются из одной SSO-сессии. Домашний интернет имеет
userType=Fttbи намеренно отклоняется. - Переключение разрешено только на номер из актуального списка аккаунтов; проверяются путь, query target и итоговый
profileSummary.ctn. - Кабинет может перевести сессию на региональный поддомен
*.beeline.ru. - Одного Chromium profile было недостаточно: сессионные cookies исчезали после перезапуска. Поэтому используется приватный
.beeline-sso-cookies.json. --restore-last-sessionоказался нестабилен и не используется.- Остатки пакетов берутся из
Accumulators. Если поле отсутствует, противоречиво или похоже на служебный sentinel, оно остаётся unavailable/неоднозначным.
Порядок проверки: основной номер → каждый связанный мобильный → снова основной → новый запуск Chromium → повтор всех целей.
- Проверяются
sessionCheck.authenticatedи номер сессии. - Разрешённые связанные номера берутся из
multiaccount/summary; результатmultiaccount/changeобязан совпасть с целью. - После switch старая вкладка и response listener закрываются, данные читаются в новой вкладке. Иначе задержанный ответ прежнего аккаунта можно ошибочно принять за новый.
- Cookie jar добавляет только отсутствующие cookies и не затирает более свежий профиль.
- Несколько параллельных копий авторизованного профиля недопустимы.
unlim: trueважнее числового остатка. Огромный sentinel не публикуется как реальные гигабайты.
Если первое переключение сбросило authenticated, снова выполните ручной вход, сохраните обновлённую сессию и повторите основной → связанные → основной.
- Прямой вход в кабинет на серверном Chromium давал
403/QRATOR. Смена VPN-маршрута не устранила причину. - Рабочий вход начинается через официальный MTS ID. Возможна ручная проверка человека.
- Для модемной, smart-device или бизнес-SIM появляется отдельный экран: устройство не может принять код, и владелец должен нажать «Получить SMS с кодом» либо использовать заранее добавленный контактный номер.
- CAPTCHA и SMS проходит только владелец. Автоматизация не должна нажимать защитные шаги или запрашивать код сама.
save_session.pyсохраняет только cookies/origins доменов MTS в.mts-state.json.- Адаптер сверяет номер по user-info и counters. После успешного входа сессия должна работать без VPN и нового SMS.
- Баланс обрезается до копеек так же, как в кабинете; интернет конвертируется из
KByte. Отсутствующие минуты/SMS остаются unavailable.
Если после успешного входа другие аккаунты показывают старый error, выполните один полный свежий poll всех аккаунтов: не редактируйте Flags вручную.
cd /srv/mbplugin/app
MBPLUGIN_DATA=/srv/mbplugin/data docker compose exec mbplugin python /app/runtime.py pollRuntime не разрешает параллельные опросы, сохраняет cooldown минимум 30 минут и прерывает зависший poll через 15 минут. Код 75 означает занято/cooldown, а не поломку оператора.
Проверьте без копирования персональных данных в терминальный отчёт:
- у каждого аккаунта свежая строка в SQLite;
- identity ответа совпадает с запрошенным номером;
- баланс совпадает с кабинетом;
0, отсутствие, безлимит, некорректность и противоречие различаются;- отрицательный баланс не превращается в «заблокирован» без явного статуса оператора.
Только после успешного ручного poll измените в runtime/compose.yaml POLL_ENABLED на 1 и пересоздайте collector. Рекомендуемый интервал — 3600 секунд; разрешённый диапазон — 1800–86400.
Создайте отдельного MQTT-пользователя с доступом только к mbplugin/#, homeassistant/+/mbplugin_+/+/config и чтением homeassistant/status в соответствии с синтаксисом ACL вашего broker. Не используйте администраторские credentials.
cd /srv/mbplugin/app
install -d -m 0700 secrets
cp /path/to/reviewed/bridge.json secrets/bridge.json
install -m 0600 /path/to/mqtt-password secrets/mqtt_password
sudo chown -R 10001:10001 secretsВозьмите схему из examples/bridge.example.json. id и alias не должны содержать номер. Укажите абсолютный database path внутри контейнера /data/store/mbplugin.db.
python -m json.tool secrets/bridge.json >/dev/null
MBPLUGIN_DATA=/srv/mbplugin/data docker compose --profile mqtt config >/dev/null
MBPLUGIN_DATA=/srv/mbplugin/data docker compose --profile mqtt up -d bridgeBridge читает SQLite в read-only transaction, публикует retained QoS 1, LWT, HA birth refresh и выполняет порядок: state → availability поля → availability аккаунта. Raw exception, номер, cookie и URL не уходят в MQTT.
Состояния диагностики ограничены: ok, stale, worker_offline, database_error, auth_required, captcha_required, parse_error, operator_error.
Для каждого аккаунта проверьте цепочку:
кабинет оператора → adapter result → SQLite → MQTT retained state → HA entity
Обязательная приёмка:
- все старые device/entity IDs сохранены;
- добавились только ожидаемые поля;
- значения MQTT и HA совпадают;
- неизменившиеся значения перепубликуются раньше
expire_after; - restart и
--force-recreateне требуют нового SMS; - HA restart/birth снова получает Discovery и states;
- остановка collector/bridge переводит данные в unavailable;
- запуск и свежий poll возвращают online;
- временный noVNC URL закрыт;
- связанные номера повторно проверены после нового browser process.
Не считать успешными только healthy, зелёный контейнер или unit tests.
cd runtime
PYTHONPATH=carrier python3 -m unittest discover -s carrier
PYTHONPATH=carrier python3 -m unittest discover -s bridge
python3 -m unittest discover -s tests
python3 -m py_compile runtime.py harden_upstream.py bridge/bridge.py carrier/*.py
docker compose config >/dev/nullТестовые номера синтетические. Перед публикацией дополнительно выполните secret/PII scan всего Git history.
| Симптом | Причина | Исправление | Как проверить |
|---|---|---|---|
| Supervisor build долго висит | тяжёлая сборка в HA; точная job-причина не доказана | отдельный Ubuntu Compose | HA healthy, Compose работает независимо |
| После прерванного старта нет Python package | runtime bootstrap принял частичную установку | зависимости только в image build | check-import, force-recreate |
| Данные пишутся рядом с кодом | upstream вычислил root по module path | явный /data |
SQLite/INI/profile переживают recreate |
| Chromium не стартует | удалён --headless=old |
--headless=new |
реальный browser smoke |
| Sandbox падает | использован неполный seccomp fragment | полный профиль Playwright | non-root Chromium без --no-sandbox |
| Профиль занят после crash | lock/hostname Chromium | стабильный hostname; сначала исключить живой процесс | restart/recreate, не удаляя cookies |
| HTTP опасно выставлен наружу | upstream имеет mutating endpoints без общей auth | loopback + read-only allowlist | GET отчёта 200, изменения 403 |
| Телефон не видит LAN noVNC | нет маршрута к домашней сети | временный HTTPS/VPN/SSH route | до входа 401, после закрытия недоступен |
| Билайн показывает расходы как баланс | postpaid API semantics | debt/spend/credit раздельно | сверка с кабинетом |
| Связанный Билайн снова просит вход | session cookies не пережили browser restart | приватный cookie jar | все цели после нового browser start |
| После switch данные другого номера | задержанный response или target не проверен | точная identity до parse | requested = actual |
| МегаФон теряет auth после switch | конфликт свежих токенов/профилей | один профиль, merge только отсутствующих cookies | основной → связанные → основной |
| МегаФон показывает огромные GB | unlimited sentinel | unlim: true → «Безлимит» |
числовой sensor unavailable |
| Билайн без минут/SMS | adapter не запросил Accumulators | запрос и parser пакетов | operator → DB → MQTT → HA |
| Нет поля превращается в ноль | ошибочный fallback | unavailable/status вместо 0 | fixtures missing/invalid |
| Минусовой баланс = блокировка | неверный вывод | только explicit operator status | Active остаётся не blocked |
| Прошлая дата самовольно перенесена | догадка вместо source fidelity | хранить исходную дату | значение совпадает с ответом |
| МТС отвечает 403 | неподходящий direct login route | официальный MTS ID SSO | появляется human check/login |
| МТС не шлёт код автоматически | interstitial для modem/business SIM | владелец нажимает кнопку | кабинет открыт, state сохранён |
| После МТС другие аккаунты error | старый неудачный poll оставил Flags | полный свежий poll | все аккаунты снова ok |
| Backup повреждён гонкой | архив писался внутри копируемого дерева | внешний каталог + tar/SHA check | sha256sum -c, tar -tzf |
Автообновление отключено. Для обновления:
- сохранить image digest и проверенный backup;
- прочитать upstream changelog;
- обновить tag/hash только в отдельной ветке;
- убедиться, что fail-closed patches применились ровно один раз;
- повторить unit, browser, каждого оператора, MQTT и HA acceptance;
- только затем заменить production image.
Никогда не выполняйте docker compose down -v, docker system prune или очистку browser profiles в рамках обычного обновления.
Rollback: остановить только новый collector/bridge, вернуть прежний image/Compose и указать отдельную восстановленную копию /data. Не распаковывать backup поверх действующего data и не открывать новую SQLite старой версией без проверки совместимости. Retained Discovery новых сущностей удаляется отдельным точечным списком topics.
- Сессии операторов истекают;
auth_requiredозначает новый ручной вход. - CAPTCHA может появиться повторно; её нельзя обходить автоматизацией.
- Сайт оператора способен изменить selectors/API. При несовпадении identity адаптер обязан завершиться ошибкой, а не публиковать чужие данные.
- Финансовые поля информационные. Этот проект не выполняет платежи и не включает услуги.
- Для нового оператора нужен отдельный adapter и отдельная приёмка.
- Alerting/автоматические платежные уведомления — отдельная задача; их нельзя молча добавлять при сборе данных.
Собственные материалы и код этого репозитория доступны по MIT License. Bundled upstream MBPlugin также MIT и сохраняет оригинальный copyright/license. См. LICENSE и THIRD_PARTY_NOTICES.md.