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