forked from jjposio/DHT22-TemperatureLogger
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathDHT22logger.py
422 lines (345 loc) · 17.3 KB
/
DHT22logger.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
# Copyright (c) 2015
# Author: Janne Posio
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE
# This script is intended to use with Adafruit DHT22 temperature and humidity sensors
# and with sensor libraries that Adafruit provides for them. This script alone, without
# sensor/s to provide data for it doesn't really do anyting useful.
# For guidance how to create your own temperature logger that makes use of this script,
# Adafruit DHT22 sensors and raspberry pi, visit :
# http://www.instructables.com/id/Raspberry-PI-and-DHT22-temperature-and-humidity-lo/
#!/usr/bin/python2
#coding=utf-8
import os
import re
import sys
import datetime
from datetime import timedelta
import json
import subprocess
import MySQLdb
import smtplib
from email.MIMEMultipart import MIMEMultipart
from email.MIMEText import MIMEText
# function for reading DHT22 sensors
def sensorReadings(gpio, sensor):
configurations = getConfigurations()
adafruit = configurations["adafruitpath"]
sensorReadings = subprocess.check_output(['sudo',adafruit,sensor,gpio])
try:
# try to read neagtive numbers
temperature = re.findall(r"Temp=(-\d+.\d+)", sensorReadings)[0]
except:
# if negative numbers caused exception, they are supposed to be positive
try:
temperature = re.findall(r"Temp=(\d+.\d+)", sensorReadings)[0]
except:
pass
humidity = re.findall(r"Humidity=(\d+.\d+)", sensorReadings)[0]
intTemp = float(temperature)
intHumidity = float(humidity)
return intTemp, intHumidity
# function for getting weekly average temperatures.
def getWeeklyAverageTemp(sensor):
weekAverageTemp = ""
date = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
delta = (datetime.date.today() - timedelta(days=7)).strftime("%Y-%m-%d 00:00:00")
try:
sqlCommand = "SELECT AVG(temperature) FROM temperaturedata WHERE dateandtime BETWEEN '%s' AND '%s' AND sensor='%s'" % (delta,date,sensor)
data = databaseHelper(sqlCommand,"Select")
weekAverageTemp = "%.2f" % data
except:
pass
return weekAverageTemp
# function that sends emails, either warning or weekly averages in order to see that pi is alive
def emailWarning(msg, msgType):
configurations = getConfigurations()
fromaddr = configurations["mailinfo"][0]["senderaddress"]
toaddrs = configurations["mailinfo"][0]["receiveraddress"]
username = configurations["mailinfo"][0]["username"]
password = configurations["mailinfo"][0]["password"]
subj = configurations["mailinfo"][0]["subjectwarning"]
if msgType is 'Info':
subj = configurations["mailinfo"][0]["subjectmessage"]
# Message to be sended with subject field
message = 'Subject: %s\n\n%s' % (subj,msg)
# The actual mail sending
server = smtplib.SMTP('put.your.mail.server.here',25)
server.starttls()
# server.login(username,password)
server.sendmail(fromaddr, toaddrs, message)
server.quit()
return
# helper function for database actions. Handles select, insert and sqldumpings. Update te be added later
def databaseHelper(sqlCommand,sqloperation):
configurations = getConfigurations()
host = configurations["mysql"][0]["host"]
user = configurations["mysql"][0]["user"]
password = configurations["mysql"][0]["password"]
database = configurations["mysql"][0]["database"]
backuppath = configurations["sqlbackuppath"]
data = ""
db = MySQLdb.connect(host,user,password,database)
cursor=db.cursor()
if sqloperation == "Select":
try:
cursor.execute(sqlCommand)
data = cursor.fetchone()
except:
db.rollback()
elif sqloperation == "Insert":
try:
cursor.execute(sqlCommand)
db.commit()
except:
db.rollback()
emailWarning("Database insert failed", "")
sys.exit(0)
elif sqloperation == "Backup":
# Getting current datetime to create seprate backup folder like "12012013-071334".
date = datetime.date.today().strftime("%Y-%m-%d")
backupbathoftheday = backuppath + date
# Checking if backup folder already exists or not. If not exists will create it.
if not os.path.exists(backupbathoftheday):
os.makedirs(backupbathoftheday)
# Dump database
db = database
dumpcmd = "mysqldump -u " + user + " -p" + password + " " + db + " > " + backupbathoftheday + "/" + db + ".sql"
os.system(dumpcmd)
return data
# function for checking log that when last warning was sended, also inserts new entry to log if warning is sent
def checkWarningLog(sensor, sensortemp):
currentTime = datetime.datetime.now()
currentTimeAsString = datetime.datetime.strftime(currentTime,"%Y-%m-%d %H:%M:%S")
lastLoggedTime = ""
lastSensor = ""
triggedLimit = ""
lastTemperature = ""
warning = ""
okToUpdate = False
# sql command for selecting last send time for sensor that trigged the warning
sqlCommand = "select * from mailsendlog where triggedsensor='%s' and mailsendtime IN (SELECT max(mailsendtime)FROM mailsendlog where triggedsensor='%s')" % (sensor,sensor)
data = databaseHelper(sqlCommand,"Select")
# If there weren't any entries in database, then it is assumed that this is fresh database and first entry is needed
if data == None:
sqlCommand = "INSERT INTO mailsendlog SET mailsendtime='%s', triggedsensor='%s', triggedlimit='%s' ,lasttemperature='%s'" % (currentTimeAsString,sensor,"0.0",sensortemp)
databaseHelper(sqlCommand,"Insert")
lastLoggedTime = currentTimeAsString
lastTemperature = sensortemp
okToUpdate = True
else:
lastLoggedTime = data[0]
lastSensor = data[1]
triggedLimit = data[2]
lastTemperature = data[3]
# check that has couple of hours passed from the time that last warning was sended.
# this check is done so you don't get warning everytime that sensor is trigged. E.g. sensor is checked every 5 minutes, temperature is lower than trigger -> you get warning every 5 minutes and mail is flooded.
try:
delta = (currentTime - lastLoggedTime).total_seconds()
passedTime = delta // 3600
if passedTime > 2:
okToUpdate = True
else:
pass
except:
pass
# another check. If enough time were not passed, but if temperature has for some reason increased or dropped 5 degrees since last alarm, something might be wrong and warning mail is needed
# TODO: Add humidity increase / decrease check as well...requires change to database as well.
if okToUpdate == False:
if "conchck" not in sensor:
if sensortemp > float(lastTemperature) + 5.0:
okToUpdate = True
warning = "NOTE: Temperature increased 5 degrees"
if sensortemp < float(lastTemperature) - 5.0:
okToUpdate = True
warning = "NOTE: Temperature decreased 5 degrees"
return okToUpdate, warning
# Function for checking limits. If temperature is lower or greater than limit -> do something
def checkLimits(sensor,sensorTemperature,sensorHumidity,sensorhighlimit,sensorlowlimit,humidityHighLimit,humidityLowLimit):
check = True
warningmsg = ""
# check temperature measurements against limits
if float(sensorTemperature) < float(sensorlowlimit):
warningmsg = "Temperature low on sensor: {0}\nTemperature: {1}\nTemperature limit: {2}\nHumidity: {3}".format(sensor,sensorTemperature,sensorlowlimit,sensorHumidity)
check = False
elif float(sensorTemperature) > float(sensorhighlimit):
warningmsg = "Temperature high on sensor: {0}\nTemperature: {1}\nTemperature limit: {2}\nHumidity: {3}".format(sensor,sensorTemperature,sensorhighlimit,sensorHumidity)
check = False
# check humidity measurements against limits
elif float(sensorHumidity) < float(humidityLowLimit):
warningmsg = "Humidity low on sensor: {0}\nTemperature: {1}\nHumidity limit: {2}\nHumidity: {3}".format(sensor,sensorTemperature,humidityLowLimit,sensorHumidity)
check = False
elif float(sensorHumidity) > float(humidityHighLimit):
warningmsg = "Humidity high on sensor: {0}\nTemperature: {1}\nHumidity limit: {2}\nHumidity: {3}".format(sensor,sensorTemperature,humidityHighLimit,sensorHumidity)
check = False
return check,warningmsg
# helper function for getting configurations from config json file
def getConfigurations():
path = os.path.dirname(os.path.realpath(sys.argv[0]))
#get configs
configurationFile = path + '/config.json'
configurations = json.loads(open(configurationFile).read())
return configurations
def main():
currentTime = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
configurations = getConfigurations()
# how many sensors there is 1 or 2
sensorsToRead = configurations["sensoramount"]
# Sensor names to add to database, e.g. carage, outside
sensor1 = configurations["sensors"][0]["sensor1"]
sensor2 = configurations["sensors"][0]["sensor2"]
# temperature limits for triggering alarms
sensor1lowlimit = configurations["triggerlimits"][0]["sensor1lowlimit"]
sensor2lowlimit = configurations["triggerlimits"][0]["sensor2lowlimit"]
sensor1highlimit = configurations["triggerlimits"][0]["sensor1highlimit"]
sensor2highlimit = configurations["triggerlimits"][0]["sensor2highlimit"]
# humidity limits for triggering alarms
sensor1_humidity_low_limit = configurations["humiditytriggers"][0]["sensor1_humidity_low_limit"]
sensor1_humidity_high_limit = configurations["humiditytriggers"][0]["sensor1_humidity_high_limit"]
sensor2_humidity_low_limit = configurations["humiditytriggers"][0]["sensor2_humidity_low_limit"]
sensor2_humidity_high_limit = configurations["humiditytriggers"][0]["sensor2_humidity_high_limit"]
# Sensor gpios
gpioForSensor1 = configurations["sensorgpios"][0]["gpiosensor1"]
gpioForSensor2 = configurations["sensorgpios"][0]["gpiosensor2"]
# Backup enabled
backupEnabled = configurations["sqlBackupDump"][0]["backupDumpEnabled"]
backupHour = configurations["sqlBackupDump"][0]["backupHour"]
# Connection check enabled
connectionCheckEnabled = configurations["connectionCheck"][0]["connectionCheckEnabled"]
connectionCheckDay = configurations["connectionCheck"][0]["connectionCheckDay"]
connectionCheckHour = configurations["connectionCheck"][0]["connectionCheckHour"]
# type of the sensor used, e.g. DHT22 = 22
sensorType = configurations["sensortype"]
# Default value for message type, not configurable
msgType = "Warning"
d = datetime.date.weekday(datetime.datetime.now())
h = datetime.datetime.now()
# check if it is 5 o clock. If yes, take sql dump as backup
if backupEnabled == "Y" or backupEnabled == "y":
if h.hour == int(backupHour):
databaseHelper("","Backup")
# check if it is sunday, if yes send connection check on 23.00
if connectionCheckEnabled == "Y" or connectionCheckEnabled == "y":
okToUpdate = False
if str(d) == str(connectionCheckDay) and str(h.hour) == str(connectionCheckHour):
try:
sensor1weeklyAverage = getWeeklyAverageTemp(sensor1)
if sensor1weeklyAverage != None and sensor1weeklyAverage != '':
checkSensor = sensor1+" conchck"
okToUpdate, tempWarning = checkWarningLog(checkSensor,sensor1weeklyAverage)
if okToUpdate == True:
msgType = "Info"
Message = "Connection check. Weekly average from {0} is {1}".format(sensor1,sensor1weeklyAverage)
emailWarning(Message, msgType)
sqlCommand = "INSERT INTO mailsendlog SET mailsendtime='%s', triggedsensor='%s', triggedlimit='%s' ,lasttemperature='%s'" % (currentTime,checkSensor,sensor1lowlimit,sensor1weeklyAverage)
databaseHelper(sqlCommand,"Insert")
except:
emailWarning("Couldn't get average temperature to sensor: {0} from current week".format(sensor1),msgType)
pass
if sensorsToRead != "1":
okToUpdate = False
try:
sensor2weeklyAverage = getWeeklyAverageTemp(sensor2)
if sensor2weeklyAverage != None and sensor2weeklyAverage != '':
checkSensor = sensor2+" conchck"
okToUpdate, tempWarning = checkWarningLog(checkSensor,sensor2weeklyAverage)
if okToUpdate == True:
msgType = "Info"
Message = "Connection check. Weekly average from {0} is {1}".format(sensor2,sensor2weeklyAverage)
emailWarning(Message, msgType)
sqlCommand = "INSERT INTO mailsendlog SET mailsendtime='%s', triggedsensor='%s', triggedlimit='%s' ,lasttemperature='%s'" % (currentTime,checkSensor,sensor2lowlimit,sensor2weeklyAverage)
databaseHelper(sqlCommand,"Insert")
except:
emailWarning( "Couldn't get average temperature to sensor: {0} from current week".format(sensor2),msgType)
pass
# default message type to send as email. DO NOT CHANGE
msgType = "Warning"
sensor1error = 0
okToUpdate = False
# Sensor 1 readings and limit check
try:
sensor1temperature, sensor1humidity = sensorReadings(gpioForSensor1, sensorType)
limitsOk,warningMessage = checkLimits(sensor1,sensor1temperature,sensor1humidity,sensor1highlimit,sensor1lowlimit,sensor1_humidity_high_limit,sensor1_humidity_low_limit)
except:
emailWarning("Failed to read {0} sensor".format(sensor1),msgType)
sensor1error = 1
pass
if sensor1error == 0:
try:
# if limits were trigged
if limitsOk == False:
# check log when was last warning sended
okToUpdate, tempWarning = checkWarningLog(sensor1,sensor1temperature)
except:
# if limits were triggered but something caused error, send warning mail to indicate this
emailWarning("Failed to check/insert log entry from mailsendlog. Sensor: {0}".format(sensor1),msgType)
sys.exit(0)
if okToUpdate == True:
# enough time has passed since last warning or temperature has increased/decreased by 5 degrees since last measurement
warningMessage = warningMessage + "\n" + tempWarning
# send warning
emailWarning(warningMessage, msgType)
try:
# Insert line to database to indicate when warning was sent
currentTime = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
sqlCommand = "INSERT INTO mailsendlog SET mailsendtime='%s', triggedsensor='%s', triggedlimit='%s' ,lasttemperature='%s'" % (currentTime,sensor1,sensor1lowlimit,sensor1temperature)
databaseHelper(sqlCommand,"Insert")
except:
# if database insert failed, send warning to indicate that there is some issues with database
emailWarning("Failed to insert from {0} to mailsendlog".format(sensor1),msgType)
# sensor 2 readings and limit check
sensor2error = 0
okToUpdate = False
if sensorsToRead != "1":
try:
sensor2temperature, sensor2humidity = sensorReadings(gpioForSensor2, sensorType)
limitsOk,warningMessage = checkLimits(sensor2,sensor2temperature,sensor2humidity,sensor2highlimit,sensor2lowlimit,sensor2_humidity_high_limit,sensor2_humidity_low_limit)
except:
emailWarning("Failed to read {0} sensor".format(sensor2),msgType)
sensor2error = 1
pass
if sensor2error == 0:
try:
if limitsOk == False:
okToUpdate, tempWarning = checkWarningLog(sensor2,sensor2temperature)
except:
emailWarning("Failed to check/insert log entry from mailsendlog. Sensor: {0}".format(sensor2),msgType)
sys.exit(0)
if okToUpdate == True:
warningMessage = warningMessage + "\n" + tempWarning
emailWarning(warningMessage, msgType)
try:
# Insert line to database to indicate when warning was sent
sqlCommand = "INSERT INTO mailsendlog SET mailsendtime='%s', triggedsensor='%s', triggedlimit='%s' ,lasttemperature='%s'" % (currentTime,sensor2,sensor2lowlimit,sensor2temperature)
databaseHelper(sqlCommand,"Insert")
except:
emailWarning("Failed to insert entry from {0} to mailsendlog".format(sensor1),msgType)
# insert values to db
try:
if sensor1error == 0:
sqlCommand = "INSERT INTO temperaturedata SET dateandtime='%s', sensor='%s', temperature='%s', humidity='%s'" % (currentTime,sensor1,sensor1temperature,sensor1humidity)
# This row below sets temperature as fahrenheit instead of celsius. Comment above line and uncomment one below to take changes into use
#sqlCommand = "INSERT INTO temperaturedata SET dateandtime='%s', sensor='%s', temperature='%s', humidity='%s'" % (currentTime,sensor1,(sensor1temperature*(9.0/5.0)+32),sensor1humidity)
databaseHelper(sqlCommand,"Insert")
if sensorsToRead != "1" and sensor2error == 0:
sqlCommand = "INSERT INTO temperaturedata SET dateandtime='%s', sensor='%s', temperature='%s', humidity='%s'" % (currentTime,sensor2,sensor2temperature,sensor2humidity)
# This row below sets temperature as fahrenheit instead of celsius. Comment above line and uncomment one below to take changes into use
#sqlCommand = "INSERT INTO temperaturedata SET dateandtime='%s', sensor='%s', temperature='%s', humidity='%s'" % (currentTime,sensor2,(sensor2temperature*(9.0/5.0)+32),sensor2humidity)
databaseHelper(sqlCommand,"Insert")
except:
sys.exit(0)
if __name__ == "__main__":
main()