Push notifications with minimal data usage
<p>In my project, I want to call an action on a Raspberry Pi from the Internet. Somehow like that:</p>

<ol>
<li>Visit webpage</li>
<li>Hit a button on the webpage</li>
<li>Raspberry pi executes a script</li>
</ol>

<p>The difficult part is, that the raspberry pi only has a mobile Internet connection <em>without a flatrate</em>. So, sending and receiving data from/to the Raspberry Pi is costly.</p>

<p>So this is my question:</p>

<p>Which technologies and patterns can I use to send push notifications to the raspberry pi <em>with minimal data usage</em>?</p>

<p>Start off by creating or starting something that accepts incoming connections on your RPi. Something small as the below example would do:</p>

<pre><code>#!/usr/bin/python
from socket import *
s = socket()
s.bind(('', 8001))
s.listen(4)
while 1:
    ns, na = s.accept()
    print(na,'sent you:',ns.recv(8192))
</code></pre>

<p>Now, the above example will only open a port on <code>8001</code> and then print whatever the other send sent to it.</p>

<p>On the server end, I suggest you setup a web server or something else that is easily accessible that can store an IP in a database/register somewhere.</p>

<p>Then, during boot of your RPi (pref after the networking service is started) go ahead and schedule:</p>

<pre><code>curl https://your-server.eu/reg_ip.php &gt; /dev/null 2&gt;&amp;1
</code></pre>

<p>Now, on the server <code>your-server.eu:443</code>, make sure <code>req_ip.php</code> saves the clients IP (client being your RPi) in a database somewhere.</p>

<p>Now, which ever application that you have that need to send out PUSH notifications can lookup the current IP of clients in the database and try to connect to port <code>8001</code> on those IP's and send whatever data you need.</p>

<p>Now two things:</p>

<ol>
<li><p>A listening TCP socket (on the RPi) won't use up any data at all but will allow for incomming connections when needed.</p></li>
<li><p>If your IP changes on the RPi (which it probably will on a moving GSM/3G/4G network for instance), you need to do another <code>curl</code> request to your server. This however could easily be tied to a for instance <code>ip monitor</code> command and perform the curl request then.</p></li>
</ol>

<h1>tl;dr</h1>

<p>Here's your chain:</p>

<p><code>Pi opens a listening socket -&gt; Pi connects to HTTP(S) -&gt; servers saves IP -&gt; when server has something to send -&gt; connect to last known IP on port X -&gt; send data -&gt; close connection</code></p>

<h1>Enhancing further</h1>

<p>Now, the HTTP header alone is quite big, in fact it's default 78 bytes of package data (TCP and IP headers usually isn't included in data rates, only the data being transferred is - in this case, HTTP data.). So what you could expand on is if you could use the simple example of a socket above on the server as well and just take the <code>na[0]</code> variable and send that to a database, that way you use literally 0 data-rate on your data subscription.</p>

<p>Only the actual data sent then later on from the server application as a "PUSH notification" would use up data.</p>

<h1>In case your RPi is on a NAT based network (private network)</h1>

<p>Seeing as the OP gets a <code>10.X.X.X</code> address it's unlikely that a listening socket will do the trick.</p>

<p>Instead, what you could do is you could simply try and establish a connection and keep it open and have the server send data over any open socket when it has data.</p>

<p>Here's an extremely rough idea of how you could achieve it.<br>
I kept it simple and stupid just to give an idea without solving the entire thing.</p>

<p>Again, the open socket between the client (RPi) and the Server won't use up any data until you actually send data over the channel.</p>

<p>You could in your server fetch data from a database that you want to send to the clients, you could do anything really. Since I don't know your goal I'll just leave it as it is for the time being.</p>

<h2>Server:</h2>

<pre><code>#!/usr/bin/python
from socket import *
from threading import *
from time import time, sleep
from random import randint

class ClientThread(Thread)
    def __init__(self, clients):
        self.clients = clients
        self.messages = []
        Thread.__init__(self)
        self.start()

    def notify(self, message):
        if type(message) is str: message = bytes(message, 'UTF-8')
        self.messages.append(message)

    def run(self):
        while 1:
            if len(self.messages) &gt; 0:
                m = self.messages.pop(0)
                for client in self.clients:
                    self.clients[client]['sock'].send(m)

class RandomMessageGenerator(Thread):
    def __init__(self, notify_func):
        self.notify_func = notify_func
        self.last = time()
        Thread.__init__(self)
        self.start()

    def run(self):
        while 1:
            self.notify_func('Time since last update: ' + str(time() - self.last))
            self.last = time()
            sleep(randint(0,30))

client_list = {}
client_thread_handle = ClientThread(client_list)
random_generator = RandomMessageGenerator(client_thread_handle.nofity)

s = socket()
s.bind(('', 8001))
s.listen(4)

while 1:
    ns, na = s.accept()
    client_list[na] = {'sock' : 'ns'}
</code></pre>

<h2>Client:</h2>

<pre><code>from socket import *
s = socket()
s.connect(('server', 8001))
while 1:
    print(s.recv(8192))
</code></pre>

python web push-notification connection network-protocols