Наступило лето и очень многие уезжают из города. Кто-то на время отпуска, а кто-то и на все лето (если работа позволяет). Но одна из главных проблем за городом (для всех людей, так или иначе связанных с IT) — отсутствие нормального проводного быстрого интернета. Но это частично решается благодаря существованию LTE-сетей.
В моем регионе крупных провайдеров LTE всего два: Мегафон и Yota. Мегафон существенно дешевле, но у него есть одна крайне неприятная особенность: ограничение в 20гб трафика в месяц даже на максимальном тарифе.
Поэтому выбор оператора, на мой взгляд, очевиден. Но все же платить за 20 мегабит в два раза больше чем за 100 дома — сомнительное удовольствие. Но при этом, в отличие от других операторов, Yota позволяет в в любой момент бесплатно изменять текущий тариф в личном кабинете с перерасчетом оплаченного времени. Нужна скорость — выкручиваем ползунок на максимум. Нет? Тогда можно снизить скорость и платить меньше. Ну как тут можно удержаться и не автоматизировать этот процесс?
На хабре уже были статьи (раз, два), описывающие попытки йотоводов-автоматизаторов. Однако, в силу некоторых особенностей, их творения мне не подошли и пришлось написать свой, существенно отличающийся, велосипедОднако, в силу некоторых особенностей, их творения мне не подошли и пришлось написать свой, существенно отличающийся, велосипед.
А начнем мы, пожалуй, с разбора причин, по которым мне не подошли чужие велосипеды. Особо нетерпеливые / материально заинтересованные могут сразу перейти к разделу, описывающему реализацию.
Постановка задачи
Вариант linx56 мне не подошел ну совсем никак, потому как он работает phantomjs, для работы которого нужно довольно нехилое железо (которым Raspberry PI не является). Да и даже если я поставлю это на свой основной компьютер и оставлю его работать круглосуточно, забив в cron расписание по которому нужно отключать интернет, то я
Второй вариант от bambrman существенно выигрывает у первого тем, что вместо phantomjs использует сильно менее прожерливый curl. Но в остальном все то же самое.
В итоге было решено написать еще один велосипед, в основе которого лежало бы не расписание (или руки человека), а текущая скорость, потребляемая всеми устройствами в локальной сети.
Реализация
Я использовал Raspberry Pi и скрипт на Python, который каждые n секунд подключается по SSH к главному домашнему роутеру (TP-LINK WDR4300 с OpenWrt на борту) и при помощи ifconfig стягивает статистику нужного интерфейса.
После этого он считает среднюю скорость за промежуток между последним и предыдущим замером и отправляет ее в модуль, который отвечает за переключение тарифов в соответствии с текущей скоростью и настройками конфига. Чтобы было понятнее, приведу здесь его часть:
modes: { 0: 320, 290: 512, 495: 768, 700: 1.0, 900: 2.1, 1900: 3.1, 2900: 4.1, 3900: 6.5, 6500: 8.0, 8000: 10.0, 10000: 15.0, 14800: 20.0 }
Как вы видите, в качестве ключа словаря выступает скорость в килобитах, а в качестве значения — тариф йоты.
По результатам замера текущей скорости у нас получилось, скажем, 1530 килобит. Округляем в меньшую сторону до значения ближайшего ключа в конфиге и получаем 900. Соответственно, должен использоваться тариф со скоростью 2.1 мегабит. В случае, если этот тариф уже активен, ничего не происходит. Но вот если тариф нужно повышать, то тут есть одна особенность.
Проблема в том, что если интернет долгое время не использовался (и активировался тариф в 320 кбит/с), а потом кто-то внезапно включил YouTube, то последовательное переключение с 320 кбит/с до 3.1 мбит/с может занять немало времени (не говоря уже о вечно лагающем ЛК Yota). С другой стороны, прыгать каждый раз на максимальный тариф, когда кто-то решил загрузить пару страниц и несколько картинок — тоже плохая идея. В общем я не придумал ничего лучше, чем добавить в конфиг
speed_increase_step: 1
Этот параметр указывает на размер шага апгрейда между тарифами. То есть при установке speed_increase_step = 5, апгрейд с 320 кбит/с произойдет сразу до 3.1 мегабит, а дальше 15.0 и 20.0. И если возможная скорость не будет использоваться полностью, это будет компенсировано механизмом даунгрейда.
Даунгрейд
В случае, если активный тариф долгое время не используется в полную силу (текущая скорость не превышает нужного для текущего тарифа порога), скрипт ждет n минут и выполняет переключение на тариф, который удовлетворяет текущим запросам локальной сети (на основании средней скорости). Это самое количество минут задается в параметре hold_high_speed_time в минутах.
hold_high_speed_time: 3
По сути, основной функционал на этом завершается. Правда, есть еще кое-что, что, возможно, кого-нибудь да заинтересует:
Уведомления о смене тарифа через PushBullet
Писалось, как и все остальное, в первую очередь для себя. Но, честно честно говоря, так и не пригодилось, потому как генерирует слишком много спама. Но может кому понравится ;)
Включается это дело все там же, в конфиге:
pb_enabled: true pb_api: v13WA7HYfwj99gUz8rwvK2m7eLN1uheVxZujAgP8gn2su pb_devices: [ ujAgP8gn2sasFDsgsWhxs, ujAgP8gn2mkasnuH2Gtga ]
Как видите, здесь все довольно просто. pb_enabled принимает значения true/false. Назначение очевидно.
pb_api — ваш ключ со страницы www.pushbullet.com/account
pb_devices — список идентификаторов устройств, на которые рассылаются уведомления. Взять можно тут: api.pushbullet.com/v2/devices. При входе вас попросят авторизоваться: просто введите ключ api в поле логина. Полное описание метода тут: docs.pushbullet.com/v2/devices
Ну что же.
Установка
- Первым делом идем и устанавливаем на свой роутер OpenWrt. Ну или что-нибудь подобное с SSH, авторизацией по ключам и утилитой ifconfig.
- После этого нужно найти что-то вроде Raspberry Pi. То, на чем можно запустить третий питон и где есть ssh-клиент с авторизацией по ключам.
- Настраиваем авторизацию по ключу для подключения к роутеру. Убеждаемся что команда вида (с вашими данными, конечно же) «ssh 192.168.1.1 -p 22 -l root ifconfig» работает без дополнительных вопросов
- Ставим Python 3 и pip. Изначально все писалось и тестировалось на 3.2.3. На совместимость с более ранними версиями тестов не было
- Клонируем репозиторий проекта с GitHub: https://github.com/wutiarn/yota-speed-controller
- Делаем cd в папку с репозиторием
- Выполняем pip3 install -r requirements.txt. Обратите внимание, что pip может иметь другое имя (pip-3.2, например)
- Редактируем config.yaml. Описания большинства его параметров есть вверху. Остальные, как мне кажется, в представлении не нуждаются. Ну разве что кроме interface. По умолчанию там указан eth0.2. Обычно в openwrt именно eth0.2 является wan-интерфейсом, но лучше проверить. Также стоит отметить опцию use_ssh. Если выставить ее в false, то совершенно логично, мониториться будет локальный интерфейс. Ну это на случай если у кого-нибудь в качестве роутера используется полноценный компьютер. Ну или кто-то запустит все это дело прямо на роутере
- После этого запускаем main.py. Можно как напрямую (предварительно дав ему права на исполнения), так и передав интерпретатору питона
- Если все работает, то устанавливаем supervisor (обычно есть в стандартных репозиториях)
- Перемещаем файл yota.conf в папку /etc/supervisor/conf.d/ попутно подправив в нем пути к main.py и, если хотите, к файлу, в который будет писаться лог
- И перезапускаем supervisor командой supervisorctl reload. Демон должен запуститься автоматически. Посмотрите напоследок логи. В них должно быть что-то вроде:
2014-07-06 14:16:01,805 [INFO] yota | Initializing 2014-07-06 14:16:11,497 [INFO] yota.web | CURRENT TARIFF: 512 2014-07-06 14:16:11,712 [INFO] yota | Initialised 2014-07-06 14:16:11,718 [INFO] yota | Starting 2014-07-06 14:16:16,016 [DEBUG] yota.current_speed_provider | Current speed: RX: 5 TX: 9. Summary: 14, Time: 3 2014-07-06 14:16:17,262 [INFO] yota.speed_control | Downgrade started: 2014-07-06 14:19:16.022307 2014-07-06 14:16:18,315 [DEBUG] yota.current_speed_provider | Current speed: RX: 7 TX: 19. Summary: 26, Time: 2 2014-07-06 14:16:20,575 [DEBUG] yota.current_speed_provider | Current speed: RX: 12 TX: 20. Summary: 32, Time: 2
Вот, собственно, и все. Понимаю, что это некоторое злоупотребление щедростью Yota (хотя, учитывая тарифы, щедростью ли?), но тем не менее смею надеяться, что никто ничего менять не собирается. В конце концов, людей, способных развернуть у себя такое, не так много. А если учесть, что мало кто из них пользуется Yota, то так и вообще мелочь.

