Created
January 10, 2014 11:05
-
-
Save fake-name/8350183 to your computer and use it in GitHub Desktop.
Minimal script to shove data into a emoncms instance.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/python | |
""" | |
All Emoncms code is released under the GNU Affero General Public License. | |
See COPYRIGHT.txt and LICENSE.txt. | |
--------------------------------------------------------------------- | |
Emoncms - open source energy visualisation | |
Part of the OpenEnergyMonitor project: | |
http://openenergymonitor.org | |
""" | |
''' | |
Modifications by Fake-Name/Connor Wolf | |
''' | |
import urllib2, httplib | |
import logging, logging.handlers | |
import time | |
import logging | |
import sys | |
import serial | |
import subprocess | |
import re | |
import datetime | |
import os | |
import signal | |
"""class ServerDataBuffer | |
Stores server parameters and buffers the data between two HTTP requests | |
""" | |
class ServerDataBuffer(): | |
def __init__(self, protocol, domain, path, apikey, period, logger=None): | |
"""Create a server data buffer initialized with server settings. | |
protocol (string): "https://" or "http://" | |
domain (string): domain name (eg: 'domain.tld') | |
path (string): emoncms path with leading slash (eg: '/emoncms') | |
apikey (string): API key with write access | |
period (int): sending interval in seconds | |
logger (string): the logger's name (default None) | |
""" | |
self._protocol = protocol | |
self._domain = domain | |
self._path = path | |
self._apikey = apikey | |
self._period = period | |
self._data_buffer = [] | |
self._last_send = time.time() | |
self._logger = logging.getLogger(logger+".EMon") | |
self._logger.debug("Initing EMonCMS interface") | |
def add_data(self, data): | |
# Append timestamped dataset to buffer. | |
# data (list): node and values (eg: '[node,val1,val2,...]') | |
self._logger.debug("Server " + self._domain + self._path + " -> add data: " + str(data)) | |
self._data_buffer.append(data) | |
def send_data(self): | |
"""Send data to server.""" | |
# Prepare data string with the values in data buffer | |
data_string = '[' | |
for data in self._data_buffer: | |
data_string += '[' | |
data_string += "0" | |
for sample in data: | |
data_string += ',' | |
data_string += str(sample) | |
data_string += '],' | |
data_string = data_string[0:-1]+']' # Remove trailing comma and close bracket | |
self._data_buffer = [] | |
self._logger.debug("Data string: " + data_string) | |
# Prepare URL string of the form | |
# 'http://domain.tld/emoncms/input/bulk.json?apikey=12345&data=[[-10,10,1806],[-5,10,1806],[0,10,1806]]' | |
url_string = self._protocol+self._domain+self._path+"/input/bulk.json?apikey="+self._apikey+"&data="+data_string | |
self._logger.debug("URL string: " + url_string) | |
# Send data to server | |
self._logger.info("Sending to " + self._domain + self._path) | |
try: | |
result = urllib2.urlopen(url_string) | |
except urllib2.HTTPError as e: | |
self._logger.warning("Couldn't send to server, HTTPError: " + str(e.code)) | |
except urllib2.URLError as e: | |
self._logger.warning("Couldn't send to server, URLError: " + str(e.reason)) | |
except httplib.HTTPException: | |
self._logger.warning("Couldn't send to server, HTTPException") | |
except Exception: | |
import traceback | |
self._logger.warning("Couldn't send to server, Exception: " + traceback.format_exc()) | |
else: | |
if (result.readline() == 'ok'): | |
self._logger.info("Send ok") | |
else: | |
self._logger.warning("Send failure") | |
# Update _last_send | |
#self._last_send = time.time() | |
def check_time(self): | |
"""Check if it is time to send data to server. | |
Return True if sending interval has passed since last time | |
""" | |
now = time.time() | |
delta = now - self._last_send | |
if (delta > self._period): | |
print "Elapsed delta between updates =", delta, "seconds. overshoot =", delta-self._period, "seconds." | |
self._last_send = self._last_send + self._period | |
return True | |
def has_data(self): | |
"""Check if buffer has data | |
Return True if data buffer is not empty. | |
""" | |
return (self._data_buffer != []) | |
def initLogging(): | |
mainLogger = logging.getLogger("Main") # Main logger | |
mainLogger.setLevel(logging.DEBUG) | |
ch = logging.StreamHandler(sys.stdout) | |
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') | |
ch.setFormatter(formatter) | |
mainLogger.addHandler(ch) | |
def timeout_command(cmdList, timeout): | |
#call shell-command and either return its output or kill it | |
#if it doesn't normally exit within timeout seconds and return None | |
start = datetime.datetime.now() | |
process = subprocess.Popen(cmdList, stdout=subprocess.PIPE, stderr=subprocess.PIPE) | |
while process.poll() is None: | |
time.sleep(0.1) | |
now = datetime.datetime.now() | |
if (now - start).seconds > timeout: | |
print "subprocess timed out. Force-killing it" | |
os.kill(process.pid, signal.SIGKILL) | |
os.waitpid(-1, os.WNOHANG) | |
return None | |
return process.stdout.read() | |
def getModlet(modletMac, outletNo="0"): | |
print "calling modlet interface" | |
powerRe = re.compile("INFO -- : (\d+)w") | |
remainingRe = re.compile("(\d+) returned, (\d+) remaining") | |
recPower = None | |
recRemain = None | |
cont = True | |
while cont: | |
ret = timeout_command(["hacklet", "read", "-n", modletMac, "-s", outletNo], 7) | |
if ret != None: | |
for line in ret.split("\n"): | |
if line: | |
print "Modlet ret = ", line | |
found = powerRe.findall(line) | |
if found: | |
recPower = int(found[0]) | |
remaining = remainingRe.findall(line) | |
if remaining: | |
recRemain = remaining | |
print "Remaining =", recRemain, "-", | |
if recRemain[0][1] == "0": # the second digit of the "remaining samples" return is 0, so the sample in recPower is the latest one | |
print "Have latest power draw numbers. Stopping" | |
cont = False # Stop looping | |
else: | |
print "Looping more because there is data in the receive buffer" | |
print "recPower", recPower, "recRemain", recRemain | |
return recPower | |
class ThermBaroLogger(): | |
def __init__(self, portStr): | |
self.port = serial.Serial(portStr, 115200, timeout=0) | |
self.tmpStr = "" | |
self.tmpLog = [] | |
self.presLog = [] | |
def procRx(self): | |
self.tmpStr += self.port.read(50) | |
if "\r" in self.tmpStr: | |
out, self.tmpStr = self.tmpStr.split("\r") | |
out = out.split() | |
temp = None | |
pres = None | |
for item in out: | |
try: | |
key, val = item.split(":") | |
#print key, float(val) | |
if key == "Temperature": | |
self.tmpLog.append(float(val)) | |
temp = float(val) | |
if key == "Pressure": | |
self.presLog.append(float(val)) | |
pres = float(val) | |
except ValueError: | |
print "Started part-way through a packet" | |
print "Pres:", temp, "Pres:", pres | |
def getValues(self): | |
if self.tmpLog == None and self.tmpLog == None: | |
print "TempBaro logging not working? Wat?" | |
return None, None | |
avgTmp = sum(self.tmpLog)/float(len(self.tmpLog)) | |
avgPrs = sum(self.presLog)/float(len(self.presLog)) | |
print "Processing", len(self.tmpLog), "temperature samples,", len(self.presLog), "pressure samples." | |
self.tmpLog = [] | |
self.presLog = [] | |
return avgTmp, avgPrs | |
if __name__ == "__main__": | |
print "Starting" | |
initLogging() | |
monBuf = ServerDataBuffer(protocol = 'https://', | |
domain = '10.1.1.39', | |
path = '/emoncms', | |
apikey = "[NOPE]", | |
period = 15, | |
logger="Main") | |
thermBaro = ThermBaroLogger('/dev/ttyUSB0') | |
# hacklet: | |
# Found device [NOT FOR YOU] on network 0xe771 | |
# hacklet read -n [NOT FOR YOU] -s 1 | |
modletMac = "[NOT FOR YOU]" | |
# Purge out all back-logged power numbers from modlets ahead of time | |
# (Also makes testing modlet stuff easier, since you don't have to wait 15 seconds) | |
print getModlet(modletMac, "0"), getModlet(modletMac, "1") | |
while 1: | |
thermBaro.procRx() | |
if monBuf.check_time(): | |
print "Ready to send" | |
avgTmp, avgPrs = thermBaro.getValues() | |
if avgTmp != None and avgPrs != None: | |
monBuf.add_data(["1", avgTmp, avgPrs]) | |
modletVal1, modletVal2 = getModlet(modletMac, "0"), getModlet(modletMac, "1") | |
if modletVal1 != None and modletVal2 != None: | |
print "Sending modlet packet" | |
monBuf.add_data(["2", modletVal1, modletVal2]) | |
monBuf.send_data() | |
time.sleep(0.05) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment