Web interface and MYSQL structures added
[weathermon.git] / weathermon
1 #!/usr/bin/python
2
3 import serial
4
5 from os import listdir
6 from os.path import isfile, join
7
8 from termios import tcflush, TCIOFLUSH
9
10 from time import sleep,time
11
12 from uuid import getnode
13
14 from hashlib import md5
15
16 import socket
17
18 import sys,traceback
19
20 import pycurl
21 from urllib import urlencode
22 from StringIO import StringIO
23
24 searchpath = '/dev/serial/by-id/'
25 baud = 9600
26 timeout = 5
27
28 external_submit_interval = 320
29 expire_interval = 1200
30 submit_time = time()
31 submit_queue = {}
32
33 import MySQLdb
34 import ConfigParser
35
36 def find_port():
37
38   global serial_num
39
40   files = listdir(searchpath)
41   for f in files:
42     if serialnum in f:
43       return join(searchpath,f)
44   return None
45
46 def open_port(path):
47
48   ser = serial.Serial(path,baud,timeout)
49   if ser.portstr:
50     tcflush(ser,TCIOFLUSH);
51   return ser
52   
53 def read_port(ser):
54
55   line = ser.readline()
56   return line.strip()
57   
58 def read_loop(ser,callback):
59
60   while True:
61   
62     try:
63       line=read_port(ser)
64       if line:
65         callback(line)
66     except KeyboardInterrupt:
67       break
68     finally:
69       None
70
71 def submit_narodmon(queue):
72
73   param = { 'ID':"{:X}".format(getnode())}
74
75   for sensor in queue:
76     value = submit_queue[sensor]['val']
77     timestamp = submit_queue[sensor]['timestamp']
78     digest = md5(sensor).hexdigest()[:18]
79     param[digest] = value;
80   
81   print param
82
83   url = "http://narodmon.ru/post.php"
84
85   try:
86       
87     response_buffer = StringIO()
88     curl = pycurl.Curl()
89                                               
90     curl.setopt(curl.URL, url)
91     curl.setopt(curl.WRITEFUNCTION, response_buffer.write)   
92     curl.setopt(curl.POST, 1)
93     curl.setopt(curl.POSTFIELDS, urlencode(param))
94                                                                   
95     curl.perform()
96     curl.close()  
97                                                                           
98     response_value = response_buffer.getvalue()
99                                                                               
100     print 'Content: ', response_value
101                                                                                   
102     return True
103                                                                                       
104   except:
105                                                                                         
106     exc_type, exc_value, exc_traceback = sys.exc_info()
107     traceback.print_tb(exc_traceback, limit=1, file=sys.stdout)
108     traceback.print_exception(exc_type, exc_value, exc_traceback,
109                               limit=2, file=sys.stdout)  
110     return False  
111                                       
112 def submit_owm(queue):
113
114   url = "http://openweathermap.org/data/post"
115   params = {'name':owm_station, 'lat':owm_lat, 'long':owm_lon}
116
117   try:
118
119     try:
120       params['temp'] = queue[owm_temp]['val']
121       params['pressure'] = queue[owm_pres]['val']
122       params['humidity'] = queue[owm_humi]['val']
123     except:
124       return False  
125
126     response_buffer = StringIO()
127     curl = pycurl.Curl()
128
129     curl.setopt(curl.URL, url)
130     curl.setopt(curl.USERPWD, '%s:%s' % (owmuser, owmpasswd))
131     curl.setopt(curl.WRITEFUNCTION, response_buffer.write)
132     curl.setopt(curl.POST, 1)
133     curl.setopt(curl.POSTFIELDS, urlencode(params))
134
135     curl.perform()
136     curl.close()
137     
138     response_value = response_buffer.getvalue()
139     
140     print 'Content: ', response_value
141   
142     return True
143   
144   except:
145
146     exc_type, exc_value, exc_traceback = sys.exc_info()
147     traceback.print_tb(exc_traceback, limit=1, file=sys.stdout)
148     traceback.print_exception(exc_type, exc_value, exc_traceback,
149                                   limit=2, file=sys.stdout)
150     return False  
151
152 def purge_queue():
153   global submit_queue
154   clean = []
155   for key in submit_queue:
156     if submit_queue[key]['timestamp'] < time()-expire_interval:
157       print "Expired value for "+key
158       clean.append(key)
159   for i in clean:
160     del submit_queue[i]    
161   
162 def submit_data(sensor_type,sensor_id,sensor_param,param_value):
163   global submit_time
164   global submit_queue
165   c = database.cursor()
166   c.execute('CALL meteo.submit_value(%s,%s,%s,%s)', (sensor_type,sensor_id,sensor_param,param_value))
167   database.commit()
168   submit_queue[sensor_type+'.'+sensor_id+'.'+sensor_param]={'val':param_value,'timestamp':time()}
169   if time()>submit_time+external_submit_interval:
170     if submit_narodmon(submit_queue):
171       if owmuser:
172         submit_owm(submit_queue)
173       print 'Purging queue...'
174       submit_time=time()
175       purge_queue()
176         
177  
178 def process_str(str):
179   try:
180     msg_type, msg_body = str.split(':')
181     if msg_type == 'STATUS':
182       print 'Status: ', msg_body
183     elif msg_type == 'ERROR':
184       print 'Error: ', msg_body
185     elif msg_type == 'SENSOR':
186       sens = msg_body.split(',')
187       sensor = {}
188       sensor_type = None
189       sensor_id = None
190       for rec in sens:
191         key,value = rec.split('=')
192         if key == 'TYPE':
193           sensor_type = value
194         elif key == 'ID':
195           sensor_id = value  
196         else:  
197           sensor[key] = value
198       if sensor_type:    
199         if not sensor_id:
200           sensor_id='DEFAULT';    
201       for key in sensor:
202         print 'Type = ', sensor_type, ', ID = ', sensor_id, ', Param = ', key, ', Value = ', sensor[key]
203         submit_data(sensor_type,sensor_id,key,sensor[key])
204   except:
205     print 'Exception processing...'
206     try:
207       database.close()
208     except:
209       None
210     reconnect()
211
212 def print_str(str):
213   print str 
214       
215 def weather_mon():
216
217   path = find_port()
218   ser = open_port(path)
219   read_loop(ser,process_str)
220
221 def reconnect():
222             
223   try:
224
225     global database
226     database = MySQLdb.connect(host=dbhost,user=dbuser,passwd=dbpasswd,use_unicode=True,connect_timeout=10)
227     database.set_character_set('utf8')
228     c = database.cursor()
229     c.execute('SET NAMES utf8;')
230     print "Database connected..."
231         
232   except:
233         
234     print "Error connecting database"
235     sleep(30)
236       
237 def main():
238   weather_mon()
239
240 try:
241
242   cfg = ConfigParser.RawConfigParser(allow_no_value=True)
243   cfg.readfp(open('/etc/weathermon.conf'))
244   dbhost = cfg.get("mysql","host")
245   dbuser = cfg.get("mysql","user")
246   dbpasswd = cfg.get("mysql","passwd")
247   serialnum = cfg.get("serial","id")
248   owmuser = cfg.get("openweathermap","user")
249   owmpasswd = cfg.get("openweathermap",'passwd')
250   if owmuser:
251     owm_temp = cfg.get("openweathermap",'temp')
252     owm_pres = cfg.get("openweathermap",'pres')
253     owm_humi = cfg.get("openweathermap",'humi')
254     owm_lat = cfg.get("openweathermap",'lat')
255     owm_lon = cfg.get("openweathermap",'lon')
256     owm_station = cfg.get("openweathermap",'station')
257   reconnect()
258  
259 except:
260
261   print "Cannot intialize system"
262   exit() 
263   
264 if __name__ == "__main__":
265   import sys
266   reload(sys)
267   sys.setdefaultencoding('utf-8')
268   main()