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