Yota Cost Optimization: Attempt # 3

Hi, Habr!

Summer has come and so many are leaving the city. Someone on vacation, and someone for the whole summer (if work allows). But one of the main problems in the country (for all people who are somehow connected with IT) is the lack of a normal wired fast Internet. But this is partially solved thanks to the existence of LTE networks.

In my region, there are only two major LTE providers: Megaphone and Yota. A megaphone is significantly cheaper, but it has one extremely unpleasant feature: a limitation of 20GB traffic per month, even at the maximum rate.

Therefore, the choice of the operator, in my opinion, is obvious. But still, paying for 20 megabits is two times more than for 100 at home - a dubious pleasure. But at the same time, unlike other operators, Yota allows at any time to change the current tariff for free in your account with recalculation of the paid time. Need speed - unscrew the slider to the maximum. Not? Then you can slow down and pay less. Well, how can one hold on and not automate this process?

There were already articles on the hub ( one , two), describing the attempts of iotovodov-automators. However, due to some features, their creations did not suit me and I had to write my own, significantly different bike; However, due to some features, their creations did not fit me and I had to write my own, significantly different bike.

And we will begin, perhaps, with an analysis of the reasons why other people's bicycles did not fit me. Particularly impatient / financially interested persons can immediately proceed to the section describing the implementation.

Formulation of the problem

The linx56 option didn’t suit me well at all, because it works with phantomjs, which requires pretty good hardware (which Raspberry PI is not). Yes, and even if I put it on my main computer and leave it to work around the clock, scoring a cron schedule for which you need to turn off the Internet, I ’ll get more electricity bills than save anyway I won’t get a normal result, because the Internet is used quite unpredictably (and not only me, but also family members, for whom the main thing is to “just work”).

The second option from bambrman significantly outperforms the first in that instead of phantomjs it uses a much less greedy curl. But the rest is the same.

As a result, it was decided to write another bike, which would not be based on a schedule (or human hands), but the current speed consumed by all devices on the local network.


I used Raspberry Pi and a Python script that connects via SSH every n seconds to the main home router (TP-LINK WDR4300 with OpenWrt on board) and with the help of ifconfig it compresses the statistics of the desired interface.

After that, he considers the average speed for the interval between the last and previous measurements and sends it to the module, which is responsible for switching tariffs in accordance with the current speed and config settings. To make it clearer, here is a part of it:
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

As you can see, the speed in kilobits is used as the dictionary key, and the iota tariff is used as the value.

According to the results of measuring the current speed, we got, say, 1530 kilobits. We round down to the value of the nearest key in the config and get 900. Accordingly, a tariff with a speed of 2.1 megabits should be used. If this tariff is already active, nothing happens. But if the tariff needs to be increased, then there is one feature.

The problem is that if the Internet has not been used for a long time (and a tariff of 320 kbps was activated), and then someone suddenly turned on YouTube, then switching from 320 kbps to 3.1 mbps can take a lot of time (not to mention the ever-lagging LC Yota). On the other hand, jumping every time at the maximum rate when someone decided to upload a couple of pages and a few pictures is also a bad idea. In general, I did not come up with anything better than adding to the config

speed_increase_step: 1

This parameter indicates the step size of the upgrade between tariffs. That is, when setting speed_increase_step = 5, an upgrade from 320 kbps will happen immediately to 3.1 megabits, and then 15.0 and 20.0. And if the possible speed is not fully used, this will be compensated by the downgrade mechanism.


If the active tariff has not been used to its full potential for a long time (the current speed does not exceed the threshold necessary for the current tariff), the script waits for n minutes and switches to a tariff that satisfies the current requests of the local network (based on average speed). This number of minutes is set in the hold_high_speed_time parameter in minutes.

hold_high_speed_time: 3

In fact, the main functionality ends here. True, there is something else that may be of interest to someone:

Tariff change notifications via PushBullet

It was written, like everything else, primarily for myself. But, frankly, it wasn’t useful, because it generates too much spam. But maybe someone will like it;)

This thing turns on all the same in the config:
pb_enabled: true
pb_api: v13WA7HYfwj99gUz8rwvK2m7eLN1uheVxZujAgP8gn2su
pb_devices: [

As you can see, everything is pretty simple here. pb_enabled is true / false. The purpose is obvious.
pb_api - your key from www.pushbullet.com/account
pb_devices - a list of identifiers of devices to which notifications are sent. You can take it here: api.pushbullet.com/v2/devices . At the entrance you will be asked to log in: just enter the api key in the login field. A full description of the method is here: docs.pushbullet.com/v2/devices

Well then. Deal done, message delivered. Now you can show the code and tell how to prepare the whole thing.


  • First of all, go and install OpenWrt on your router. Well, or something similar with SSH, key authorization and ifconfig utility.
  • After that, you need to find something like the Raspberry Pi. This is where you can run the third python and where there is an ssh client with key authorization.
  • Configure key authorization to connect to the router. We make sure that the command of the form (with your data, of course) "ssh -p 22 -l root ifconfig" works without additional questions
  • We put Python 3 and pip. Initially, everything was written and tested on 3.2.3. For compatibility with earlier versions of the tests was not
  • We clone the project repository from GitHub: https://github.com/wutiarn/yota-speed-controller
  • Make cd in the repository folder
  • We execute pip3 install -r requirements.txt. Note that pip may have a different name (pip-3.2, for example)
  • Editing config.yaml. Descriptions of most of its parameters are at the top. The rest, it seems to me, do not need an introduction. Well, except for interface. By default, eth0.2 is specified there. Normally, in openwrt, eth0.2 is the wan interface, but it is better to check. Also worth noting is the use_ssh option. If you set it to false, then it is completely logical that the local interface will be monitored. Well, this is in case someone uses a full-fledged computer as a router. Well, or someone will run the whole thing right on the router
  • After that, run main.py. You can either directly (after giving him the right to execute), or passing the python to the interpreter
  • If everything works, then install supervisor (usually in standard repositories)
  • We move the yota.conf file to the /etc/supervisor/conf.d/ folder along the way by correcting the paths to main.py in it and, if you want, the file to which the log will be written
  • And restart supervisor with the supervisorctl reload command. The daemon should start automatically. Look at the last logs. They should have something like:
    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

That, in fact, is all. I understand that this is some abuse of Yota's generosity (although, considering the tariffs, is it generosity?), But nevertheless I dare to hope that no one is going to change anything. In the end, there are not many people who are able to deploy this in themselves. And if you consider that few of them use Yota, then it’s a trifle in general.

Also popular now: