Hi everyone,
I’m new to this forum. Want to share a technique I am using to track my sleep hours.
I was looking for a way to track when I fell asleep & woke up. I looked at various apps and wearables but didn’t see anything that suited me well. For example, some wearables consider you to be asleep when you are lying in bed reading a book, and others were not so good privacy-wise.
Theoretically I could just manually record the time when I go to bed and when I wake up, but that is not accurate if I have difficulty falling asleep, or if I wake up in the middle of the night, or simply forget because I am tired, etc…
I realized that checking my email is the first thing I do when I wake up, and the last thing I do before going to sleep. So, I simply made a tiny POP email server that my phone automatically pings whenever I check my email. That server simply records the timestamp each time. Looking at the data, it is pretty obvious when I am awake and when I am sleeping (in context with the rest of the data I am tracking).
I am sharing the Python source code below. Once this script is running (on an EC2 server or anywhere), you just go into your phone’s settings and add a new email account. You can actually enter any bogus username and password. The important thing is to make it only ping when you manually check your email (in iOS, choose “Manual” instead of “Fetch”/“Push”). Then you get automatic logging to a CSV file each time you check your email. Hope this is useful to someone
'''
Fake email server that writes to a CSV file each time you check your email.
Just run it on any server such as Amazon EC2 (must allow traffic on port 110),
using the filename of the script:
sudo python3 fake_email_server.py
Then add a POP3 email account on your phone using your server's IP address.
You can enter any username/password; it accepts any credentials.
adapted from http://code.activestate.com/recipes/534131-pypopper-python-pop3-server/
'''
import logging
import time
import socket
import sys
import select
import pytz
from datetime import datetime
logging.basicConfig(format="%(name)s %(levelname)s - %(message)s")
log = logging.getLogger("pypopper")
log.setLevel(logging.INFO)
CSV_PATH = 'email_checks.csv'
TIMEZONE = pytz.timezone('US/Eastern')
HOST = '0.0.0.0' # wildcard IP address
PORT = 110 # the default POP3 port
SOCKET_LIFETIME = 15
END = "\r\n"
class ConnectionWrapper:
'''need wrapper rather than subclass because we don't instantiate it directly'''
_timeout = 10
def __init__(self, conn: socket.socket):
self.conn = conn
# seconds for one operation until it tries again
conn.settimeout(self._timeout)
def sendall(self, data: str):
if len(data) < 50:
log.debug("send: %r", data)
else:
log.debug("send: %r...", data[:50])
data += END
self.conn.sendall(data.encode())
def recvall(self) -> str:
data = []
start = time.time()
while time.time() - start < self._timeout:
chunk = self.conn.recv(4096).decode('utf-8')
if END in chunk:
data.append(chunk[: chunk.index(END)])
break
data.append(chunk)
if len(data) > 1:
pair = data[-2] + data[-1]
if END in pair:
data[-2] = pair[: pair.index(END)]
data.pop()
break
log.debug("recv: %r", "".join(data))
return "".join(data)
def close(self):
return self.conn.close()
def get_ip_address(self):
return self.conn.getsockname()[0]
class ClientClosedSocket(Exception):
pass
def serve(host, port):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind((host, port))
try:
if host:
hostname = host
else:
hostname = "localhost"
log.info("POP3 serving on %s:%s", hostname, port)
while True:
sock.listen(1)
inner_conn, addr = sock.accept()
conn = ConnectionWrapper(inner_conn)
# record the current time stamp and IP address to file
ip_address = conn.get_ip_address()
timestamp = datetime.now(TIMEZONE).strftime('%Y-%m-%d %H:%M:%S %Z')
with open(CSV_PATH, 'a') as f:
f.write('{},{}\n'.format(timestamp, ip_address))
log.debug('Connected by %s', addr)
try:
conn.sendall("+OK pop3 server ready")
start = time.time()
while time.time() - start < SOCKET_LIFETIME:
try:
_timeout_seconds = 1
readable, writable, in_error = select.select(
[inner_conn], [inner_conn], [], _timeout_seconds
)
except OSError:
print('OSError...socket closed from client side')
raise ClientClosedSocket
if not readable:
continue
# will raise select.error
data = conn.recvall()
if not data:
log.warning('Socket is supposedly readable but got no data')
continue
command = data.split(maxsplit=1)[0]
log.info(f'Received command: {command}')
response = dict(
USER="+OK user accepted",
PASS="+OK pass accepted",
STAT="+OK 0 0",
LIST="+OK\r\n.\r\n",
NOOP="+OK",
QUIT="+OK pypopper POP3 server signing off",
).get(command, "-ERR unknown command")
conn.sendall(response)
if command == 'QUIT':
break
log.info('Closing socket due to timeout')
except ClientClosedSocket:
pass
finally:
conn.close()
except (SystemExit, KeyboardInterrupt):
log.info("pypopper stopped")
except Exception as ex:
print('exception is', ex)
log.critical("fatal error", exc_info=ex)
finally:
print('about to do sock.shutdown')
sock.shutdown(socket.SHUT_RDWR)
sock.close()
if __name__ == '__main__':
serve(HOST, PORT)