# HG changeset patch # User Matt Johnston # Date 1433863664 -28800 # Node ID 26eee8591f6160b99f7d55334c7e45858ba1c14e # Parent 6d06795aefbb93356d530264101c96ea28f21a59 long polling works diff -r 6d06795aefbb -r 26eee8591f61 py/config.py --- a/py/config.py Mon Jun 08 22:33:04 2015 +0800 +++ b/py/config.py Tue Jun 09 23:27:44 2015 +0800 @@ -20,9 +20,9 @@ INTERNAL_TEMPERATURE = '/sys/class/thermal/thermal_zone0/temp' HMAC_KEY = "a key" -SERVER_URL = 'https://evil.ucc.asn.au/~matt/templog/update' +SERVER_URL = 'https://evil.ucc.asn.au/~matt/templog' UPDATE_URL = "%s/update" % SERVER_URL -SETTINGS_URL = "%s/update" % SERVER_URL +SETTINGS_URL = "%s/get_settings" % SERVER_URL # site-local values overridden in localconfig, eg WORT_NAME, HMAC_KEY try: diff -r 6d06795aefbb -r 26eee8591f61 py/configwaiter.py --- a/py/configwaiter.py Mon Jun 08 22:33:04 2015 +0800 +++ b/py/configwaiter.py Tue Jun 09 23:27:44 2015 +0800 @@ -1,42 +1,58 @@ -class ConfigWaiter(object): - """ Waits for config updates from the server. http long polling """ +import asyncio +import aiohttp - def __init__(self, server): - self.server = server - self.epoch_tag = None - self.http_session = aiohttp.ClientSession() +import utils +from utils import L,D,EX,W,E +import config + +class ConfigWaiter(object): + """ Waits for config updates from the server. http long polling """ - @asyncio.coroutine - def run(self): - # wait until someting has been uploaded (the uploader itself waits 5 seconds) - yield from asyncio.sleep(10) - while True: - yield from self.do() + def __init__(self, server): + self.server = server + self.epoch_tag = None + self.http_session = aiohttp.ClientSession() - # avoid spinning too fast - yield from server.sleep(1) + @asyncio.coroutine + def run(self): + # wait until someting has been uploaded (the uploader itself waits 5 seconds) + yield from asyncio.sleep(10) + while True: + yield from self.do() + + # avoid spinning too fast + yield from asyncio.sleep(1) - @asyncio.coroutine - def do(self): - try: - if self.epoch_tag: - headers = {'etag': self.epoch_tag} - else: - headers = None + @asyncio.coroutine + def do(self): + try: + if self.epoch_tag: + headers = {'etag': self.epoch_tag} + else: + headers = None + + r = yield from asyncio.wait_for( + self.http_session.get(config.SETTINGS_URL, headers=headers), + 300) + D("waiter status %d" % r.status) + if r.status == 200: + rawresp = yield from asyncio.wait_for(r.text(), 300) - r = yield from asyncio.wait_for( - self.http_session.get(config.SETTINGS_URL, headers=headers), - 300) - if r.status == 200: - resp = yield from asyncio.wait_for(r.json(), 300) + resp = utils.json_load_round_float(rawresp) - self.epoch_tag = resp['epoch_tag'] - epoch = self.epoch_tag.split('-')[0] - if self.server.params.receive(resp['params'], epoch): - self.server.reload_signal(True) + self.epoch_tag = resp['epoch_tag'] + D("waiter got epoch tag %s" % self.epoch_tag) + epoch = self.epoch_tag.split('-')[0] + if self.server.params.receive(resp['params'], epoch): + self.server.reload_signal(True) + elif r.status == 304: + pass + else: + # longer timeout to avoid spinning + yield from asyncio.sleep(30) - except Exception as e: - E("Error watching config: %s" % str(e)) + except Exception as e: + E("Error watching config: %s" % str(e)) diff -r 6d06795aefbb -r 26eee8591f61 py/fridge.py --- a/py/fridge.py Mon Jun 08 22:33:04 2015 +0800 +++ b/py/fridge.py Tue Jun 09 23:27:44 2015 +0800 @@ -22,6 +22,7 @@ def on(self): self.turn(True) + pass def off(self): self.turn(False) diff -r 6d06795aefbb -r 26eee8591f61 py/params.py --- a/py/params.py Mon Jun 08 22:33:04 2015 +0800 +++ b/py/params.py Tue Jun 09 23:27:44 2015 +0800 @@ -2,8 +2,9 @@ import collections import json import signal -import io import tempfile +import os +import binascii import config from utils import W,L,E,EX @@ -26,7 +27,7 @@ def __init__(self): self.update(_FIELD_DEFAULTS) - self._epoch = None + self._set_epoch(None) def __getattr__(self, k): return self[k] @@ -36,9 +37,13 @@ self[k] self[k] = v + def _set_epoch(self, epoch): + # since __setattr__ is overridden + object.__setattr__(self, '_epoch', epoch) + def _do_load(self, f): try: - u = json.load(f) + u = utils.json_load_round_float(f.read()) except Exception as e: raise self.Error(e) @@ -48,7 +53,8 @@ if k not in self: raise self.Error("Unknown parameter %s=%s in file '%s'" % (str(k), str(u[k]), getattr(f, 'name', '???'))) self.update(u) - self._epoch = utils.hexnonce() + # new epoch, 120 random bits + self._set_epoch(binascii.hexlify(os.urandom(15)).decode()) L("Loaded parameters") L(self.save_string()) @@ -87,12 +93,12 @@ return ta == tb - if self.keys() != new_params.keys(): - diff = self.keys() ^ new_params.keys() + if self.keys() != params.keys(): + diff = self.keys() ^ params.keys() E("Mismatching params, %s" % str(diff)) return False - for k, v in new_params.items(): + for k, v in params.items(): if not same_type(v, self[k]): E("Bad type for %s" % k) return False @@ -100,17 +106,19 @@ dir = os.path.dirname(config.PARAMS_FILE) try: t = tempfile.NamedTemporaryFile(prefix='config', + mode='w+t', dir = dir, delete = False) - t.write(json.dumps(new_params, sort_keys=True, indent=4)+'\n') + out = json.dumps(params, sort_keys=True, indent=4)+'\n' + t.write(out) name = t.name t.close() os.rename(name, config.PARAMS_FILE) return True except Exception as e: - E("Problem: %s" % e) + EX("Problem: %s" % e) return False def save_string(self): diff -r 6d06795aefbb -r 26eee8591f61 py/requirements.txt --- a/py/requirements.txt Mon Jun 08 22:33:04 2015 +0800 +++ b/py/requirements.txt Tue Jun 09 23:27:44 2015 +0800 @@ -4,6 +4,6 @@ # sha256: 2zFqD89UuXAsr2ymGbdr4l1T9e4Hgbr_C7ni4DVfryQ python-daemon==2.0.5 -# sha256: IzjIUGznhTrC3377pzGj_QFafuJWGvqw1p3e-0NAP1o -aiohttp==0.14.4 +# sha256: 6vR5rMmP_uCgKYgkZevyHzwwLhuUpBsWyKWmlbxhSQA +aiohttp==0.16.3 diff -r 6d06795aefbb -r 26eee8591f61 py/sensor_ds18b20.py --- a/py/sensor_ds18b20.py Mon Jun 08 22:33:04 2015 +0800 +++ b/py/sensor_ds18b20.py Tue Jun 09 23:27:44 2015 +0800 @@ -35,7 +35,7 @@ def run(self): while True: yield from self.do() - yield from self.server.sleep(config.SENSOR_SLEEP) + yield from asyncio.sleep(config.SENSOR_SLEEP) @asyncio.coroutine diff -r 6d06795aefbb -r 26eee8591f61 py/tempserver.py --- a/py/tempserver.py Mon Jun 08 22:33:04 2015 +0800 +++ b/py/tempserver.py Tue Jun 09 23:27:44 2015 +0800 @@ -18,6 +18,7 @@ import sensor import params import uploader +import configwaiter class Tempserver(object): @@ -141,7 +142,7 @@ parser = argparse.ArgumentParser() parser.add_argument('--hup', action='store_true') parser.add_argument('--new', action='store_true') - parser.add_argument('--daemon', action='store_true') + parser.add_argument('-D', '--daemon', action='store_true') parser.add_argument('-d', '--debug', action='store_true') parser.add_argument('-t', '--test', action='store_true') args = parser.parse_args() diff -r 6d06795aefbb -r 26eee8591f61 py/uploader.py --- a/py/uploader.py Mon Jun 08 22:33:04 2015 +0800 +++ b/py/uploader.py Tue Jun 09 23:27:44 2015 +0800 @@ -22,7 +22,7 @@ yield from asyncio.sleep(5) while True: yield from self.do() - yield from self.server.sleep(config.UPLOAD_SLEEP) + yield from asyncio.sleep(config.UPLOAD_SLEEP) def get_tosend(self, readings): tosend = {} @@ -62,12 +62,13 @@ try: readings = self.server.take_readings() tosend = self.get_tosend(readings) + D("tosend >>>%s<<<" % str(tosend)) nreadings = len(readings) yield from self.send(tosend) readings = None D("Sent updated %d readings" % nreadings) except Exception as e: - E("Error in uploader: %s" % str(e)) + EX("Error in uploader: %s" % str(e)) finally: if readings is not None: self.server.pushfront(readings) diff -r 6d06795aefbb -r 26eee8591f61 py/utils.py --- a/py/utils.py Mon Jun 08 22:33:04 2015 +0800 +++ b/py/utils.py Tue Jun 09 23:27:44 2015 +0800 @@ -5,6 +5,7 @@ import select import logging import binascii +import json D = logging.debug L = logging.info @@ -134,5 +135,6 @@ except Exception as e: return -1 -def hexnonce(): - return binascii.hexlify(os.urandom(120)) + +def json_load_round_float(s, **args): + return json.loads(s,parse_float = lambda f: round(float(f), 2), **args)