Nginx+Mariadb+PHP+Python

目前把整個溫濕度所需要安裝的套件,設定的內容及程式原始碼,透過Ansible去處理,使用方式和moodlebox一樣,使用moodlebox的原始程式,修改成go38box,整個ansible的資料夾架構及設定方式與moodlebox相同,安裝方式請參考

網站架構

溫濕度網站的架構非常的簡單,就只是一般的網頁,此網頁樣版來自於https://templated.co/azure,只做小部分修改,在溫濕度圖的地方使用兩個iFrame把溫度與濕度的html嵌入到首頁中,然後固定時間會重新載入溫濕度的html檔,所以在整個網站顯示的部分沒什麼特別的程式,也沒有使用到php,會安裝php主要是因為使用phpmyadmin去管理設定資料庫

相關檔案

  • 網站路徑:/var/www/html/temperature

  • 首頁:index.html

  • 溫度:db_temp.html

  • 濕度:db_humidity.html

這邊要注意的部分為db_temp.html及db_humidity.html兩個檔案,此兩個檔案為link,來源為/home/go38box/DHT22/db_temp.html/home/go38box/DHT22/db_humidity.html,另外phpmyadmin也是用link的方式建立,來源為/usr/share/phpmyadmin,檔案清單如下:

go38box@go38box:/var/www/html/temperature $ ls -al
total 52
drwxrwxr-x 6 www-data www-data  4096 Apr 26 00:33 .
drwxr-xr-x 3 root     root      4096 Apr 26 00:33 ..
drwxrwxr-x 3 www-data www-data  4096 Apr 26 00:33 css
lrwxrwxrwx 1 root     root        36 Apr 26 00:33 db_humidity.html -> /home/go38box/DHT22/db_humidity.html
lrwxrwxrwx 1 root     root        32 Apr 26 00:33 db_temp.html -> /home/go38box/DHT22/db_temp.html
drwxrwxr-x 8 www-data www-data  4096 Apr 26 00:33 .git
drwxrwxr-x 2 www-data www-data  4096 Apr 26 00:33 images
-rw-rw-r-- 1 www-data www-data  2199 Apr 26 00:33 index.html
drwxrwxr-x 2 www-data www-data  4096 Apr 26 00:33 js
-rw-rw-r-- 1 www-data www-data 17128 Apr 26 00:33 LICENSE.txt
lrwxrwxrwx 1 root     root        21 Apr 26 00:33 phpmyadmin -> /usr/share/phpmyadmin
-rw-rw-r-- 1 www-data www-data    12 Apr 26 00:33 README.md
go38box@go38box:/var/www/html/temperature $

index.html HEAD

下方內容放在<head>內,主要功能為固定時間讓iframe重新載入來更新溫濕度,setInterval為設定多久執行一次,設定方式為(秒*1000),如下(60秒*1000),就是每分鐘重新載入一次

<script language="javascript">
    one=function(){
            iftemp.location.reload()
            ifhumidity.location.reload()
    }
    setInterval("one()",60000)
</script>

index.html BODY

下方內容放在<body>內,主要顯示溫濕度的html檔,有兩個<iframe>,name分別為iftemp及ifhumidity,這個名稱需要和<head>的設定相同

<section class="content">
    <h2>溫濕度計</h2>
    <iframe name="iftemp" src="../db_temp.html" width="550px" height="260px" frameborder="0" scrolling="no"></iframe>
    <iframe name="ifhumidity" src="../db_humidity.html" width="550px" height="260px" frameborder="0" scrolling="no"></iframe>
</section>

Nginx設定

設定檔「/etc/nginx/sites-available/default」,內容如下,主要修改了root,預設的網站根目錄為「/var/www/html」目前修改成「/var/www/html/temperature」,用ansible安裝是不需要再手動設定

#
server {
        listen 80 default_server;
        listen [::]:80 default_server;

        root /var/www/html/temperature;

        index index.php index.html index.htm index.nginx-debian.html;

        server_name go38box;

        location / {
                try_files $uri $uri/ =404;
        }

        location ~ [^/]\.php(/|$) {
                include fastcgi_params;
                fastcgi_split_path_info ^(.+\.php)(/.+)$;
                fastcgi_read_timeout    300;
                fastcgi_pass    unix:/var/run/php/php7.0-fpm.sock;
                fastcgi_index   index.php;
                fastcgi_param   PATH_INFO       $fastcgi_path_info;
                fastcgi_param   SCRIPT_FILENAME $document_root$fastcgi_script_name;
                fastcgi_param   PHP_VALUE "max_execution_time=300\n upload_max_filesize=50M\n post_max_size=50M";
                client_max_body_size    50M;
        }

}

資料庫

DHT22所讀取的溫濕度會寫到Raspberry PI本機中建立的mariadb資料庫中,可以透過下方的網址及帳號密碼登入觀看及設定

目前建立一個名稱為temperature的資料庫,裡面有3張資料表,欄位說明及結構如下

資料表 temp

temp儲存溫濕度的記錄,欄位說明如下:

  • datetime:資料的日期時間

  • temp:溫度,取到小數點第一位

  • humidity 濕度,取到小數點第一位

資料結構如下圖:

資料表 smtp

smtp資料表,存放有關寄送溫濕度異常E-MAIL的設定,此表中只會有一筆設定資料,如果要修改參數可以直接修改欄位內容即可

  • id:此id為key值,因為此資料表為設定用,所以只需要一筆資料,程式會讀取id=0的該筆資料當做設定值

  • hostname:網站網址,目前預設為 go38box.local

  • from_addr:寄件者E-MAIL,目前使用gmail當作smtp來寄送mail

  • to_addr:收件者E-MAIL

  • smtp_addr:gmail的smtp網址為 smtp.gmail.com

  • smtp_port:gmail的smtp port為 587

  • smtp_password:smtp的密碼,使用gmail當作smtp寄送mail,所以此密碼為from_addr 的E-MAIL密碼,目前此欄位有加密

  • send_time:最後一次寄送E-Mail的時間(程式會自動更新此欄位)

  • send_status:目前溫濕度狀態,1為異常、0為正常,此欄位請不要修改(程式會自動更新此欄位)

  • send_wait_time:每執行一次都會檢查是否異常,此欄位儲存單位是秒數,主要在溫濕度一直在異常狀態下不會重復的寄送,例如此欄位目前預設值為3600秒,也就是1小時,當送出一封異常E-MAIL後,會自動更新欄位中的send_time最後送出異常E-Mail的時間,每次執行會用目前的時間和send_time相減,看是否超過此欄位所設定的時間(1小時),如果超過則會再寄送一次異常E-MAIL

  • send_check_time:每執行一次都會檢查是否正常,此欄位儲存單位是秒數,如果上一次寄送異常E-MAIL的時間和目前時間相比是否大於所設定的send_check_time,大於的話才會切換為正常,必免溫濕度剛好在設定的邊界值跳動會一直送出異常與正常的E-MAIL,例如目前設定660,代表11分鐘內,不會寄送恢復正常的E-MAIL,如果超過,則會更新send_timesend_status設為0

資料表 config

  • id:此id為key值,因為此資料表為設定用,所以只需要一筆資料,程式會讀取id=0的該筆資料當做設定值

  • send_mail:此欄位主要設定是否要寄送E-MAIL,0(不寄送),1(寄送)

  • temp_max:設定溫度正常範圍的最高溫度,目前預設為30度

  • temp_min:設定溫度正常範圍的最低溫度,目前預設為10度

  • humidity_max:設定濕度正常範圍的最高濕度,目前預設為70%

  • humidity_min:設定濕度正常範圍的最低濕度,目前預設為40%

  • time_offset:當程式取得新的溫濕度後,會先抓資料庫中最後一筆資料回來做參考比對,檢查與最後一筆資料的時間是否超過time_offset的設定值,目前預設為100秒,如果與上一筆資料時間相差超過100秒,則會直接寫入資料庫,如果沒有超過,則會再比對目前溫濕度是否和最後一筆的溫濕度相差超過difference的設定值,如果超過也是不寫入資料庫中,主要就是去DHT22元件的異常值

  • difference:溫濕度的正負差,目前設定為5

  • thingspeak_key:如果有申請thingspeak,也想把資料上傳到thingspeak的話,註冊後會取得一組thingspeak_key,要上傳的話把key填入此欄位,如果不想上傳的話就清空此欄位,請參考文內的ThingSpeak 說明

Python 主程式

在開始前,請先閱讀開發環境及設備取得溫濕度 這兩個章節,確認溫濕度元件已接上RaspberryPi並且可以讀取到溫濕度

#!/usr/bin/python
# coding:utf-8

import sys, os, logging
import Adafruit_DHT
import httplib, urllib
import time,datetime
import MySQLdb
import base64
import smtplib
import pandas as pd
from bokeh.models import BoxAnnotation
from bokeh.plotting import figure, output_file, show
from bokeh.models import HoverTool
from Crypto.Cipher import DES
from email.MIMEMultipart import MIMEMultipart
from email.MIMEText import MIMEText

# 程式檔案路徑
fPath = "/home/go38box/DHT22/"

# log 參數設定
FORMAT = '%(asctime)s - %(message)s'
logpath = fPath + "tempLogs"
logging.basicConfig(format = FORMAT, filename = logpath)
logger = logging.getLogger('tcpserver')
"""
console = logging.StreamHandler()
aformat = logging.Formatter('%(asctime)s - %(message)s')
console.setFormatter(aformat)
logger.addHandler(console)
"""

def main():
    writeToDb = False
    errId = -1

    # 連接資料庫
    db = db_connect()
    # 取得最後一筆資料
    lastRecord = get_last_record(db)
    # 取得config資料表中的設定
    [dbSendMail,dbTempMax,dbTempMin,dbHumidityMax,dbHumidityMin,dbTimeOffset,dbDifference,dbThingSpeakApiKey] = get_config(db)
    #取得目前時間
    dtnow = datetime.datetime.now()
    #取得溫濕度
    [temp,humidity] = dht22() 
    # 目前時間和最後一筆資料的時間相差幾秒
    timeoffset = (dtnow - lastRecord[0]).seconds

    # 送溫濕度到ThingSpeak,如果設定值為空的話,就不傳送
    if dbThingSpeakApiKey!='':
        params = urllib.urlencode({'field1': '{0:0.1f}'.format(temp), 'field2': '{0:0.1f}'.format(humidity), 'key': dbThingSpeakApiKey})
        post_to_thingspeak2(params)

    #溫濕度在0~100間才處理
    if (humidity >0 and humidity <= 100) and (temp >0 and temp<=100):
        if timeoffset > dbTimeOffset: 
            #目前時間和資料庫中最後一筆資料的時間超過X秒就直接寫入資料庫
            logger.warning('Msg =>last(%s Temp:%s Humidity:%s) New(%s Temp:%6.2f Humidity:%6.2f) event:%s', lastRecord[0],lastRecord[1],lastRecord[2],dtnow,temp,humidity,'timeoffset > 60')
            writeToDb = True
            errId=2
        else: 
            #目前時間和資料庫在X秒內,就檢查和上一筆的溫濕度不可超過所設定的範圍
            #超過則不寫入資料庫
            #溫度+-5度 ,濕度+-5度
            #正負值由資料庫中設定
            if (temp > lastRecord[1]+dbDifference) or (temp < lastRecord[1]-dbDifference): 
                logger.warning('Msg =>last(%s Temp:%s Humidity:%s) New(%s Temp:%6.2f Humidity:%6.2f) event:%s', lastRecord[0],lastRecord[1],lastRecord[2],dtnow,temp,humidity,'(temp > lastRecord[1]+'+str(dbDifference)+') or (temp < lastRecord[1]-'+str(dbDifference)+')')
                writeToDb = False
                errId=3
            elif (humidity > lastRecord[2]+dbDifference) or (humidity < lastRecord[2]-dbDifference):
                logger.warning('Msg =>last(%s Temp:%s Humidity:%s) New(%s Temp:%6.2f Humidity:%6.2f) event:%s', lastRecord[0],lastRecord[1],lastRecord[2],dtnow,temp,humidity,'(humidity > lastRecord[2]+'+str(dbDifference)+') or (humidity < lastRecord[2]-'+str(dbDifference)+')')
                writeToDb = False
                errId=4
            else:
                writeToDb = True
    else:
        # 溫濕度不在0~100間
        logger.warning('Msg =>last(%s Temp:%s Humidity:%s) New(%s Temp:%6.2f Humidity:%6.2f) event:%s', lastRecord[0],lastRecord[1],lastRecord[2],dtnow,temp,humidity,'(humidity >0 and humidity <= 100) and (temp >0 and temp<=100)')    
        writeToDb = False
        errId=1

    # 依上方的判斷(writeToDb),決定是否要寫入資料庫
    if writeToDb:    
        #logger.warning('Msg =>last(%s Temp:%s Humidity:%s) New(%s Temp:%6.2f Humidity:%6.2f) event:%s', lastRecord[0],lastRecord[1],lastRecord[2],dtnow,temp,humidity,'OK')
        # 將目前的溫濕度寫入資料庫
        insert_to_mysql(db,dtnow,temp,humidity);
        # 取得目前時間
        dataframe = select_data(db,1)
        lastRecordNew = get_last_record(db)
        build_temp(dataframe[0],lastRecordNew,dbTempMax,dbTempMin)
        build_humidity(dataframe[1],lastRecordNew,dbHumidityMax,dbHumidityMin)
    # 依資料表config中欄位「send_mail」的設定,判斷是否要寄送E-Mail    
    if dbSendMail==1 :
        if writeToDb == True and (lastRecordNew[1]>=dbTempMin and lastRecordNew[1]<=dbTempMax and lastRecordNew[2]>=dbHumidityMin and lastRecordNew[2]<=dbHumidityMax):
            send_email(db,lastRecord[0],lastRecord[1],lastRecord[2],lastRecordNew[0],lastRecordNew[1],lastRecordNew[2],dbTempMax,dbTempMin,dbHumidityMax,dbHumidityMin,dbTimeOffset,dbDifference,timeoffset,0)    
        elif writeToDb == False:
            # print 'writeToDb=false'
            send_email(db,lastRecord[0],lastRecord[1],lastRecord[2],dtnow,temp,humidity,dbTempMax,dbTempMin,dbHumidityMax,dbHumidityMin,dbTimeOffset,dbDifference,timeoffset,errId)
        elif lastRecordNew[1]<dbTempMin or lastRecordNew[1]>dbTempMax or lastRecordNew[2]<dbHumidityMin or lastRecordNew[2]>dbHumidityMax :
            # print 'maxmine'
            errId=5
            logger.warning('Msg =>last(%s Temp:%s Humidity:%s) New(%s Temp:%6.2f Humidity:%6.2f) event:%s', lastRecord[0],lastRecord[1],lastRecord[2],dtnow,temp,humidity,'溫濕度異常,不在所設定的範圍內!')    
            send_email(db,lastRecord[0],lastRecord[1],lastRecord[2],lastRecordNew[0],lastRecordNew[1],lastRecordNew[2],dbTempMax,dbTempMin,dbHumidityMax,dbHumidityMin,dbTimeOffset,dbDifference,timeoffset,errId)

    db.close()

# 遲接資料庫
def db_connect():
    db = MySQLdb.connect(host="localhost",user="go38box", passwd="Go38box4$",db="temperature", charset="utf8")
    return db

# 取得溫濕度
def dht22():
    humidity, temperature = Adafruit_DHT.read_retry(Adafruit_DHT.AM2302,4)

    if humidity is not None and temperature is not None:
        print('Temp={0:0.1f}* Humidity={1:0.1f}%'.format(temperature,humidity))
        return(temperature,humidity)
    else:
        print('Failed to get reading. Try again!')
        return(0,0)

# 將溫濕度送到ThingSpeak上
def post_to_thingspeak2(payload):
        headers = {"Content-type": "application/x-www-form-urlencoded","Accept": "text/plain"}
        not_connected = 1
        while (not_connected):
            try:
                conn = httplib.HTTPConnection("api.thingspeak.com",80,True,10)
                conn.connect()
                not_connected = 0
            except (httplib.HTTPException, socket.error) as ex:
                # logger.warning('Error: %s',ex)
                print "Error: %s" % ex

        conn.request("POST", "/update", payload, headers)
        response = conn.getresponse()
        print( response.status, response.reason, payload, time.strftime("%c"))
        if response.status!=200 and response.reason!='OK':
            logger.warning('ThingSpeak Error: status:%s  reason:%s. payload:%s time:%s' ,response.status, response.reason, payload, time.strftime("%c"))

        data = response.read()
        conn.close()

# 將溫濕度寫入資料庫中 
def insert_to_mysql(db,dtnow,temp,humidity):
    cursor = db.cursor()
    strnow = dtnow
    strtemp = "%.1f" %(temp)
    strhumidity = "%.1f" %(humidity)
    insert = ("INSERT INTO temp (datetime, temp, humidity) VALUES (%s, %s, %s)")
    data = (strnow,strtemp,strhumidity)
    cursor.execute(insert,data)
    db.commit()

# 從資料庫中取得最後一筆溫濕度記錄
def get_last_record(db):
    cursor = db.cursor()
    cursor.execute("select * from temp order by datetime desc limit 1")
    record = cursor.fetchone()
    lastTime = record[0]
    lastTemp = record[1]
    lastHumidity = record[2]
    # print "Last Datetime = %s  Temp = %s  Humidity = %s" %(lastTime,lastTemp,lastHumidity)
    return (lastTime,lastTemp,lastHumidity)

# 從資料庫查詢從目前時間往前x天的溫濕度資料,畫圖用
def select_data(db,sDay):
    cursor = db.cursor()
    cursor.execute("select * from temp where datetime BETWEEN DATE_SUB(NOW(),INTERVAL " + str(sDay)  +" DAY) AND NOW()")
    rows = cursor.fetchall()
    #for record in rows:
    #    print "%s %s %s" % (record[0],record[1],record[2])

    df = pd.DataFrame([[ij for ij in i] for i in rows])
    df.rename(columns={0: 'DateTime', 1: 'Temp', 2: 'Humidity'}, inplace=True);
    df_temp = df.sort_values(['DateTime'], ascending=[1]);
    df_humidity = df.sort_values(['DateTime'], ascending=[2]);
    return (df_temp,df_humidity)

# 產生溫度的html檔
def build_temp(df,lastRecord,dbTempMax,dbTempMin):
    hover = HoverTool(tooltips=[
        ("時間", "@x{%F %H:%M:%S}"),
        ("溫度", "@y{%0.2f} (°C)"),
    ],
    formatters={
        'x' : 'datetime', 
        'y' : 'printf',
    },)

    TOOLS = ["pan,wheel_zoom,box_zoom,reset,save",hover]
    p = figure(plot_width=540,plot_height=250,x_axis_type="datetime",y_range=(0, 40), tools=TOOLS,toolbar_location = "above", title="溫度  ("+str(lastRecord[0])+"  "+str(lastRecord[1])+"°C)")
    p.xgrid.grid_line_color=None
    p.ygrid.grid_line_alpha=0.5
    p.xaxis.axis_label = '時間'
    p.yaxis.axis_label = '攝氏(°C)'

    p.line(df['DateTime'], df['Temp'], color='navy', alpha=0.5)
    #p.line(df['DateTime'], df['Temp'], line_dash="4 4", line_width = 1,color='navy', alpha=0.5)
    #p.circle(df['DateTime'],df['Temp'], size=10, color="navy", alpha=0.5)
    p.add_layout(BoxAnnotation(top=dbTempMin, fill_alpha=0.1, fill_color='red'))
    p.add_layout(BoxAnnotation(bottom=dbTempMin, top=dbTempMax, fill_alpha=0.1, line_color='olive', fill_color='olive'))
    p.add_layout(BoxAnnotation(bottom=dbTempMax, fill_alpha=0.1, fill_color='red'))

    output_file(fPath+"db_temp.html")
    show(p)

# 產生濕度的html檔
def build_humidity(df,lastRecord,dbHumidityMax,dbHumidityMin):
    hover = HoverTool(tooltips=[
        ("時間", "@x{%F %H:%M:%S}"),
        ("濕度", "@y{%0.2f} (%)"),
    ],
    formatters={
        'x' : 'datetime', 
        'y' : 'printf',
    },)

    TOOLS = ["pan,wheel_zoom,box_zoom,reset,save",hover]
    p = figure(plot_width=540,plot_height=250,x_axis_type="datetime",y_range=(20,105), tools=TOOLS,toolbar_location = "above", title="濕度  ("+str(lastRecord[0])+"  "+str(lastRecord[2])+"%)")
    p.xgrid.grid_line_color=None
    p.ygrid.grid_line_alpha=0.5
    p.xaxis.axis_label = '時間'
    p.yaxis.axis_label = '百分比(%)'

    p.line(df['DateTime'], df['Humidity'], color='navy', alpha=0.5)
    p.add_layout(BoxAnnotation(top=dbHumidityMin, fill_alpha=0.1, fill_color='red'))
    p.add_layout(BoxAnnotation(bottom=dbHumidityMin, top=dbHumidityMax, fill_alpha=0.1, line_color='olive', fill_color='olive'))
    p.add_layout(BoxAnnotation(bottom=dbHumidityMax, fill_alpha=0.1, fill_color='red'))

    output_file(fPath+"db_humidity.html")
    show(p)

# 讀取資料庫中config資料表中的設定,id=0
def get_config(db):
    cursor = db.cursor()
    cursor.execute("select * from config where id=0")
    record = cursor.fetchone()
    return(record[1],record[2],record[3],record[4],record[5],record[6],record[7],record[8])

# 傳送email
def send_email(db,lastDatetime,lastTemp,lastHuminidy,newDatetime,newTemp,newHuminidy,dbTempMax,dbTempMin,dbHumidityMax,dbHumidityMin,dbTimeOffset,dbDifference,timeoffset,errId):
    cursor = db.cursor()
    cursor.execute("select * from smtp where id=0")
    record = cursor.fetchone()

    fromaddr = record[2]
    toaddr = record[3]
    pwd = decrypt_des(record[6])

    sendTime = record[7]
    sendStatus = record[8]
    sendWaitTime = record[9]
    sendCheckTime = record[10]

    sendTimeOffset = (newDatetime - sendTime).seconds
    goSendMail = False

    print 'errId=' +str(errId) 
    print 'sendTimeOffset=' +str(sendTimeOffset) 

    if errId==0:
        #errId=0代表溫濕度正常,但還要加入檢查,如果上一次寄送異常mail的時間和目前時間相比是否大於所設定的sendCheckTime,大於的話才切換為正常
        if sendStatus==1 and (sendTimeOffset > sendCheckTime):
            update_smtp_send_status(db,newDatetime,0)
            #傳送回復正常的mail
            msg = MIMEMultipart()
            msg['From'] = fromaddr
            msg['To'] = toaddr
            msg['Subject'] = "Go38 溫濕度正常"

            bodytable =  """<font color="#00A800">溫濕度正常!</font><br>"""

            bodytable += """
            <br><font color="blue">*溫度正常範圍:""" + str(dbTempMin) + """ ~ """ + str(dbTempMax) + """ (°C)</font><br>
            <font color="blue">*濕度正常範圍:""" + str(dbHumidityMin) + """ ~ """ + str(dbHumidityMax) + """ (%)</font><br>
            """

            bodytable += """
            <table align="left" border="1" cellpadding="1" cellspacing="1" style="width:500px">
                <tbody>
                    <tr>
                        <td style="text-align:center"><a href="http://"""+str(record[1])+""" " target="_blank">"""+str(record[1])+"""</a></td>
                        <td style="text-align:center">日期</td>
                        <td style="text-align:center">溫度(°C)</td>
                        <td style="text-align:center">濕度(%)</td>
                    </tr>
                    <tr>
                        <td style="text-align:center">上一筆資料</td>
                        <td style="text-align:center">"""+ str(lastDatetime) +"""</td>
                        <td style="text-align:center">"""+ str(lastTemp) +"""</td>
                        <td style="text-align:center">"""+ str(lastHuminidy) +"""</td>
                    </tr>
                    <tr>
                        <td style="text-align:center">最新資料</td>
                        <td style="text-align:center">"""+ str(newDatetime) +"""</td>
                        <td style="text-align:center">"""+ str(newTemp) +"""</td>
                        <td style="text-align:center">"""+ str(newHuminidy) +"""</td>
                    </tr>
                </tbody>
            </table>
            """
            msg.attach(MIMEText(bodytable, 'html', 'utf-8') )
            server = smtplib.SMTP(record[4], record[5])
            server.starttls()
            server.login(fromaddr, pwd)
            text = msg.as_string()
            server.sendmail(fromaddr, toaddr, text)
            server.quit()

    else:    
        #最後傳送狀態,0:傳送正常,1:傳送異常
        if sendStatus == 0:
            goSendMail = True
        elif sendStatus == 1:
            #最後一次送mail的時間和目前時間相減,相差的秒數大於所設定的sendWaitTime,則再傳送一次異常mail
            if sendTimeOffset > sendWaitTime:
                goSendMail = True

        ##傳送溫濕度異常的mail
        if goSendMail==True:
            update_smtp_send_status(db,newDatetime,1)
            msg = MIMEMultipart()
            msg['From'] = fromaddr
            msg['To'] = toaddr
            msg['Subject'] = "Go38 溫濕度異常"

            #body = str(record[1]) + "MAIL測試"


            if errId==1:
                bodytable =  """<font color="red">偵測器回傳的溫濕度不在 0~100 之間,未寫入資料庫!</font><br>"""
            elif errId==2:
                bodytable =  """<font color="red">目前時間和資料庫中最後一筆資料的時間超過"""+str(dbTimeOffset)+"""秒就直接寫入資料庫,不比對正負值!</font><br>"""
            elif errId==3:
                bodytable =  """<font color="red">目前溫度與最後一筆資料相比,在"""+str(timeoffset)+"""秒內超過所設定的正負值"""+str(dbDifference)+"""!</font><br>"""
            elif errId==4:
                bodytable =  """<font color="red">目前濕度與最後一筆資料相比,在"""+str(timeoffset)+"""秒內超過所設定的正負值"""+str(dbDifference)+"""!</font><br>"""
            elif errId==5:
                bodytable =  """<font color="red">目前溫度或濕度不在所設定的正常範圍內!</font><br>"""
            else:
                bodytable =  """<font color="red">沒有此errId<br>"""

            bodytable += """
            <br><font color="blue">*溫度正常範圍:""" + str(dbTempMin) + """ ~ """ + str(dbTempMax) + """ (°C)</font><br>
            <font color="blue">*濕度正常範圍:""" + str(dbHumidityMin) + """ ~ """ + str(dbHumidityMax) + """ (%)</font><br>
            """

            bodytable += """
            <table align="left" border="1" cellpadding="1" cellspacing="1" style="width:500px">
                <tbody>
                    <tr>
                        <td style="text-align:center"><a href="http://"""+str(record[1])+""" " target="_blank">"""+str(record[1])+"""</a></td>
                        <td style="text-align:center">日期</td>
                        <td style="text-align:center">溫度(°C)</td>
                        <td style="text-align:center">濕度(%)</td>
                    </tr>
                    <tr>
                        <td style="text-align:center">上一筆資料</td>
                        <td style="text-align:center">"""+ str(lastDatetime) +"""</td>
                        <td style="text-align:center">"""+ str(lastTemp) +"""</td>
                        <td style="text-align:center">"""+ str(lastHuminidy) +"""</td>
                    </tr>
                    <tr>
                        <td style="text-align:center">最新資料</td>
                        <td style="text-align:center">"""+ str(newDatetime) +"""</td>
                        <td style="text-align:center">"""+ str(newTemp) +"""</td>
                        <td style="text-align:center">"""+ str(newHuminidy) +"""</td>
                    </tr>
                </tbody>
            </table>
            """

            # msg.attach(MIMEText(body, 'plain'))
            msg.attach(MIMEText(bodytable, 'html', 'utf-8') )
            # msg = MIMEText(mail_msg, 'html', 'utf-8') 
            server = smtplib.SMTP(record[4], record[5])
            server.starttls()
            server.login(fromaddr, pwd)
            text = msg.as_string()
            server.sendmail(fromaddr, toaddr, text)
            server.quit()

# 更新資料表smtp中的SendTime與SendStatus
def update_smtp_send_status(db,dSendTime,dSendStatus):
    cursor = db.cursor()
    cursor.execute("update smtp set send_time='"+ str(dSendTime) +"' , send_status='"+str(dSendStatus)+"' where id=0")
    db.commit()

# 解密,主要用於資料表中smtp的smtp_password欄位,加密過程還加了一組key,目前設定為"@Go38Box"
def decrypt_des(encrypted):
    if encrypted is None:
        return ""
    try:
        key = '@Go38Box'
        # ECB方式
        generator = DES.new(key, DES.MODE_ECB)
        # 解碼
        crypted_str = base64.b64decode(encrypted)
        # 解密
        result = generator.decrypt(crypted_str)
        # 替換非空白字元
        result = result.strip("")
        #print "encrypted : "+str(encrypted)+"  cipher :"+result
        return result
    except Exception, e:
        print Exception, ":", e
        return ""

# end main
if __name__ == "__main__":
    sys.exit(main())

加解密程式

以下的程式主要是用在smtp資料表欄位「smtp_password」,注意裡面有一個變數key,下方測試是使用「1234A#CD」,而溫濕度程式在使用的是「@Go38Box」

#!/usr/bin/python
# coding:utf-8

from Crypto.Cipher import DES
import base64

def encrypt_des(cipher):
    if cipher is None:
        return ""
    try:
        key = '1234A#CD'
        # ECB方式
        generator = DES.new(key, DES.MODE_ECB)
        # 未滿8位數補空白
        pad = 8 - len(cipher) % 8
        pad_str = ""
        for i in range(pad):
            pad_str = pad_str + chr(pad)
        # 加密
        encrypted = generator.encrypt(cipher + pad_str)
        # 編碼
        result = base64.b64encode(encrypted)
        print "cipher : "+str(cipher)+"  encrypted : "+result
        # decrypt_des(result)
        return result
    except Exception, e:
        print Exception, ":", e
        return ""

def decrypt_des(encrypted):
    if encrypted is None:
        return ""
    try:
        key = '1234A#CD'
        # ECB方式
        generator = DES.new(key, DES.MODE_ECB)
        # 解碼
        crypted_str = base64.b64decode(encrypted)
        # 解密
        result = generator.decrypt(crypted_str)
        # 替換非空白字元
        result = result.strip("")
        print "encrypted : "+str(encrypted)+"  cipher :"+result
        return result
    except Exception, e:
        print Exception, ":", e
        return ""

encrypt_des('123345678')
decrypt_des('y6xH9zUHiHXjh8Y5CxVMjg==')

以上程式執行結果

cipher : 123345678  encrypted : y6xH9zUHiHXjh8Y5CxVMjg==
encrypted : y6xH9zUHiHXjh8Y5CxVMjg==  cipher :123345678
>>>

Last updated