#!/usr/bin/python

import serial

from os import listdir,system
from os.path import isfile, join

from pprint import pprint

from termios import tcflush, TCIOFLUSH

from time import sleep,time

from uuid import getnode

from hashlib import md5

import socket

import sys,traceback

import pycurl
from urllib import urlencode
from StringIO import StringIO

searchpath = '/dev/serial/by-id/'
baud = 57600
path = None
timeout = 5

external_submit_interval = 320
expire_interval = 1200
submit_time = time()
submit_queue = {}

import MySQLdb
import ConfigParser

def find_port():

  global serial_num
  global path

  files = listdir(searchpath)
  for f in files:
    if serialnum in f:
      return join(searchpath,f)
  return None

def open_port(path):

  ser = serial.Serial(path,baud,timeout)
  if ser.portstr:
    tcflush(ser,TCIOFLUSH);
  return ser
  
def read_port(ser):

  line = ser.readline()
  return line.strip()
  
def read_loop(ser,callback):

  while True:
  
    try:
      line=read_port(ser)
      if line:
        callback(line)
    except KeyboardInterrupt:
      break
    finally:
      None

def print_log(str):
  global logging
  print str
  if logging == "on":
    system("logger -t weathermon \""+str+"\"")

def submit_narodmon(queue):

  param = { 'ID':"{:X}".format(getnode())}

  pprint(queue)

  for sensor in queue:
    value = submit_queue[sensor]['val']
    timestamp = submit_queue[sensor]['timestamp']
    digest = md5(sensor).hexdigest()[:18]
    param[digest] = value;
  
  pprint (param)

  url = "http://narodmon.ru/post.php"

  try:
      
    response_buffer = StringIO()
    curl = pycurl.Curl()
                                              
    curl.setopt(curl.URL, url)
    curl.setopt(curl.WRITEFUNCTION, response_buffer.write)   
    curl.setopt(curl.POST, 1)
    curl.setopt(curl.POSTFIELDS, urlencode(param))
                                                                  
    curl.perform()
    curl.close()  
                                                                          
    response_value = response_buffer.getvalue()
                                                                              
    print_log('Content: '+response_value)
                                                                                  
    return True
                                                                                      
  except:
                                                                                        
    exc_type, exc_value, exc_traceback = sys.exc_info()
    traceback.print_tb(exc_traceback, limit=1, file=sys.stdout)
    traceback.print_exception(exc_type, exc_value, exc_traceback,
                              limit=2, file=sys.stdout)  
    return False  
                                      
def submit_owm(queue):

  url = "http://openweathermap.org/data/post"
  params = {'name':owm_station, 'lat':owm_lat, 'long':owm_lon}

  try:

    try:
      params['temp'] = queue[owm_temp]['val']
      params['pressure'] = queue[owm_pres]['val']
      params['humidity'] = queue[owm_humi]['val']
    except:
      return False  

    response_buffer = StringIO()
    curl = pycurl.Curl()

    curl.setopt(curl.URL, url)
    curl.setopt(curl.USERPWD, '%s:%s' % (owmuser, owmpasswd))
    curl.setopt(curl.WRITEFUNCTION, response_buffer.write)
    curl.setopt(curl.POST, 1)
    curl.setopt(curl.POSTFIELDS, urlencode(params))

    curl.perform()
    curl.close()
    
    response_value = response_buffer.getvalue()
    
    print_log('Content: '+response_value)
  
    return True
  
  except:

    exc_type, exc_value, exc_traceback = sys.exc_info()
    traceback.print_tb(exc_traceback, limit=1, file=sys.stdout)
    traceback.print_exception(exc_type, exc_value, exc_traceback,
                                  limit=2, file=sys.stdout)
    return False  

def purge_queue():
  global submit_queue
  clean = []
  for key in submit_queue:
    if submit_queue[key]['timestamp'] < time()-expire_interval:
      print_log("Expired value for "+key)
      clean.append(key)
  for i in clean:
    del submit_queue[i]    
  
def submit_data(sensor_type,sensor_id,sensor_param,param_value):
  global submit_time
  global submit_queue
  c = database.cursor()
  c.execute('CALL meteo.submit_value(%s,%s,%s,%s,NULL)', (sensor_type,sensor_id,sensor_param,param_value))
  database.commit()
  submit_queue[sensor_type+'.'+sensor_id+'.'+sensor_param]={'val':param_value,'timestamp':time()}
  if time()>submit_time+external_submit_interval:
    if submit_narodmon(submit_queue):
      if owmuser:
        submit_owm(submit_queue)
      print_log('Purging queue...')
      submit_time=time()
      purge_queue()
        
 
def process_str(str):
  print_log("Received: "+str)
  try:
    msg_type, msg_body = str.split(':')
  except:
    return
  try:  
    if msg_type == 'STATUS':
      print_log('Status: '+msg_body)
    elif msg_type == 'ERROR':
      print_log('Error: '+ msg_body)
    elif msg_type == 'SENSOR':
      sens = msg_body.split(',')
      sensor = {}
      sensor_type = None
      sensor_id = None
      for rec in sens:
        key,value = rec.split('=')
        if key == 'TYPE':
          sensor_type = value
        elif key == 'ID':
          sensor_id = value  
        else:  
          sensor[key] = value
      if sensor_type:    
        if not sensor_id:
          sensor_id='DEFAULT';    
      for key in sensor:
	if sensor[key] is not None:
          print_log('Type = '+sensor_type+', ID = '+sensor_id+', Param = '+key+', Value = '+sensor[key])
          submit_data(sensor_type,sensor_id,key,sensor[key])
        else:
          print_log('Error: got empty parameter value for '+sensor_type+'.'+sensor_id+'.'+key)
  except:
    print_log('Exception processing...')
    exc_type, exc_value, exc_traceback = sys.exc_info()
    traceback.print_tb(exc_traceback, limit=1, file=sys.stdout)
    traceback.print_exception(exc_type, exc_value, exc_traceback,
                              limit=5, file=sys.stdout)  
    try:
      database.close()
    except:
      None
    reconnect()

def weather_mon():

  global path

  if path is None:
    path = find_port()
  ser = open_port(path)
  read_loop(ser,process_str)

def reconnect():

  connected = False

  while not connected:            

    try:

      global database
      database = MySQLdb.connect(host=dbhost,user=dbuser,passwd=dbpasswd,use_unicode=True,connect_timeout=10)
      database.set_character_set('utf8')
      c = database.cursor()
      c.execute('SET NAMES utf8;')
      print_log("Database connected...")
      connected = True 
  
    except:
        
      print_log("Error connecting database")
      sleep(30)
      
def main():
  weather_mon()

try:

  cfg = ConfigParser.RawConfigParser(allow_no_value=True)
  cfg.readfp(open('/etc/weathermon.conf'))
  dbhost = cfg.get("mysql","host")
  dbuser = cfg.get("mysql","user")
  dbpasswd = cfg.get("mysql","passwd")
  try:
    path = cfg.get("serial","port");
  except:
    path = None
  try:    
    serialnum = cfg.get("serial","id")
  except:
    serialnum = None  
  try:
    logging = cfg.get("logging","enabled")
  except:
    logging = None
  owmuser = cfg.get("openweathermap","user")
  owmpasswd = cfg.get("openweathermap",'passwd')
  if owmuser:
    owm_temp = cfg.get("openweathermap",'temp')
    owm_pres = cfg.get("openweathermap",'pres')
    owm_humi = cfg.get("openweathermap",'humi')
    owm_lat = cfg.get("openweathermap",'lat')
    owm_lon = cfg.get("openweathermap",'lon')
    owm_station = cfg.get("openweathermap",'station')
  reconnect()
 
except:

  print_log("Cannot intialize system")
  exit() 
  
if __name__ == "__main__":
  import sys
  reload(sys)
  sys.setdefaultencoding('utf-8')
  main()