メールを送る(7)

今回はHPの一番下に、”Mail” ボタンを追加しています。このボタンを押すとメールが送信されます。

メールの送信は、”お知らせメールを出したい(msmtp)” を参考にしています。リンクのページを参考にターミナルからメールを出せる状態にして下さい。送信するメールのフォーマットとして下記の様なファイルを用意しました。

mail.org
 
to: XXXXXX@yyyyyy
from: aaaaaa@yahoo.co.jp
Subject: 送信テスト

ファイルの送信テストです。
  • XXXXX@yyyyyに宛先を指定
  • aaaaa@yahoo.co.jpに自分のメールアドレスを指定。今回はYahooメールを利用しているので、@以下はyahoo.co.jpになります。
  • 件名に”送信テスト”と記入しています。
  • 本文は、”ファイルの送信テストです。”。この後に温度の測定結果を測定時間を添付しています。

仮にこのファイル名を”test.mail”とすると、ターミナルで”msmtp XXXXX@yyyyy <test.mail”を実行すればメールが送信されます。Pythonでシェルコマンドを実行するには大きく分けて”subprocess.run()”か”subprocess.Popen()”が使われます。

  • ”subprocess.run()”
    • 公式のドキュメントでサブプロセスを起動する時に使うことを推奨
    • Python3.6から使える。主にシェルコマンドを実行する。
    • 終了ステータス、標準出力、標準エラー出力を取得できる。
  • ”subprocess.Popen()”
    • シェル起動後にシェルの終了を待たず後続処理を待たない(run()は待つ)
    • インターネットラジオ等終わりの無いアプリ(ストリーミング等)で使用
    • communicateメソッドを使うとシェルの終了を待ち後続処理を実行できる。=>run()と同じ
      • 終了ステータス、標準出力、標準エラー出力を取得できる。
    • Popen オブジェクト
      • Popen.poll()
        • 子プロセスが終了しているかどうかを調べます。
        • returncode 属性を設定して返します。
        • そうでなければ None を返します。
      • Popen.wait(timeout=None)
        • 子プロセスが終了するまで待ちます。returncode 属性を設定して返します。
      • Popen.kill()
        • 子プロセスを kill します。
      • Popen.communicate(input=None, timeout=None)
        • 子プロセスが終わるまで待つ

今回は”subprocess.run()”を使用してクライアントが”Mail”ボタンを押すと、サーバはそれに答えて宛先にメールが送信される仕組みです。

プログラムの説明

先ずは、HTMLファイル。”index_07.html”

index_07.html

<!doctype html>
<html>
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width,initial-scale=1">
        <link rel="stylesheet" type="text/css" href="./http_server_06.css" >
        <title>http-server 1.0</title>
    </head>
    <body>
        <center>
            <div class="b_frame">
   	        <div class="t_font"><u>HTTP-Server 1.0</u></div><br>
		        <form method='get'>

                  <div style="display:flex; justify-content:space-evenly;"> 
        	        <button type='submit' name='1' value='0' >On</button>
        	        <button type='submit' name='2' value='0' >Clear</button>
                  </div>

		          <p style="font-size:25px;" id='1'>Tenp:</p>
    		      <button type='submit' name='3' value='0' >Measure</button>

		          <div class="disp-group"> 
		            <p style="font-size:25px;">Interval</p>
			        <div> 
                        <input type='number' max='23' min='0' id='2' class='inp_box' />(H)&nbsp;&nbsp;
			            <input type='number' max='59' min='0' id='3' class='inp_box' />(M) 
                    </div>
		          </div>
                
		          <div style="display:flex; justify-content:space-evenly;"> 
    	              <button type='submit' name='4' value='0' onclick='onBtn_Set_Inter()' >Start</button>
    	              <button type='submit' name='5' value='0' >Stop</button>
		          </div>

		          <a style="font-size:25px;color:#00ffff;cursor: pointer;" href='./data.txt' download='data.txt'>Download00</a>

                  <button type='submit' name='6' value='0' >Mail</button>

                </form>
            </div>
        </center>
      <script src="http_server_06.js"></script>
    </body>
</html>
  • 前回からの変更は、38行目にメール送信用のボタンを追加したのみ。ボタンのNameは6です。

次はPythonファイル。”hello_07.py”

hello_07.py

from http.server import HTTPServer, SimpleHTTPRequestHandler
from urllib.parse import urlparse,parse_qs

import RPi.GPIO as GPIO

import datetime

import smbus

import subprocess
from subprocess import PIPE

btn_stat = 0
run_stat = 0
time_no = 0
hr = 0
mn = 0

GPIO.setmode(GPIO.BCM)      
GPIO.setup(17, GPIO.OUT)   
GPIO.output(17,0)        
GPIO.setup(4, GPIO.IN, GPIO.PUD_UP) 
    
bus = smbus.SMBus(1)

f = open('data.txt', 'w')
f.close()

class MyHandler(SimpleHTTPRequestHandler):

    def do_GET(self):
        global btn_stat, run_stat, hr, mn, f
            
        self.send_response(200)

        fl = 1
        if self.chk_file() == 0:
            parsed = urlparse(self.path)
            params = parse_qs(parsed.query)
            a=next(iter(params))

        #-----  LED ON/OFF  --------------
            if a == "1":
                if btn_stat == 0:
                    GPIO.output(17,1)
                    print("LED_On\n")
                    btn_stat = 1
                else:
                    GPIO.output(17,0)
                    print("LED_Off\n")
                    btn_stat = 0

        #-----  Port Clear  -----------------
            elif a == "2":
                GPIO.cleanup()  
                print("IO_Clear\n")

        #-----  Measure temp  -----------------
            elif a == "3":
                print("Measurement\n")

        #-----  Start  -----------------
            elif a == "4":
                run_stat = 1 
                b = params['4'][0]
                b = b.split(",")
                hr = int(b[0])
                mn = int(b[1]) 
 
                time_no = 0
                self.DS3231_init()
                
                f = open('data.txt', 'w')
                f.close()

                self.measure_next(123)
                a = bus.read_byte_data(0x68, 0x0e)
                a |= 2
                bus.write_byte_data(0x68, 0x0e, a)
                GPIO.add_event_detect(4, GPIO.FALLING, callback=self.measure_next)

        #-----  Stop   -----------------
            elif a == "5":
                run_stat = 0
                a = bus.read_byte_data(0x68, 0x0e)
                a &= 0xfd
                bus.write_byte_data(0x68, 0x0e, a)
                GPIO.remove_event_detect(4)
                print("Stop\n")

        #-----  Send Mail   -----------------
            elif a == "6":
                cmd="cp -f mail.org mail.txt"
                proc = subprocess.run(cmd,shell=True)

                dt_now = datetime.datetime.now()               
                a = self.get_temp()
                f = open('mail.txt', 'a')
                f.write("測定時間:" + dt_now.strftime('%Y年%m月%d日 %H:%M:%S') + '\n温度:' + str(f'{a:.1f}') +'\n')
                f.close()

                cmd="msmtp XXXXX@yyyyy <mail.txt"
                proc = subprocess.run(cmd,shell=True)
                print("Stop\n")

        #-----  Data transfer  --------------
            elif a == "80":
                print("Data transfer\n")
                self.send_header('Content-type', "text/plain")
                self.end_headers()

                a = self.get_temp()
                buf = str(btn_stat) + ',' + str(f'{a:.1f}') + ',' + str(hr) + ',' + str(mn) + ',' + str(run_stat) + ','
                self.wfile.write(buf.encode())

                fl = 0
        #-------------------------------------

        else :
            fl = 0

        if fl == 1:
            f = open("index_07.html",'rb')
            self.send_response(200)
            self.send_header('Content-type', "text/html")
            self.end_headers()
            self.wfile.write(f.read())
            f.close()

    #-------------------------------------------------
    def chk_file(self):

        a=0
        if self.path == "/":
            self.path = "/index_07.html"
            dataType = "text/html"
            a=1
        if self.path.endswith(".css"):
            dataType = "text/css"
            a=1
    
        if self.path.endswith(".js"):
            dataType = "application/javascript"
            a=1
    
        if self.path.endswith(".txt"):
            dataType = "txt/plain"
            a=1
    
        if a == 1:
            self.path = "." + self.path
            f = open(self.path,'rb')
            self.send_header('Content-type', dataType)
            self.end_headers()
            self.wfile.write(f.read())
            f.close()
        return a

    #-------------------------------------------------
    def get_temp(self):
        global bus

        # 13bit mode
        bus.write_byte_data(0x48, 0x03, 0x00)
        word_data = bus.read_word_data(0x48, 0x00)

        data = (word_data & 0xff00) >> 8 | (word_data & 0xff) << 8
        data /= 128
        return(data) 

    #-------------------------------------------------

    def DS3231_init(self):

        for a in range(3):
            bus.write_byte_data(0x68, a, 0)

        a = bus.read_byte_data(0x68, 0x0d)
        a |= 0x80
        bus.write_byte_data(0x68, 0x0d, a)

    #-------------------------------------------------
    def measure_next(self,dummy):
        global hr, mn, bus, f, time_no

        d_min = bus.read_byte_data(0x68, 0x01)
        d_min = self.chg_dcb(d_min)
        d_hour = bus.read_byte_data(0x68, 0x02)
        d_hour = self.chg_dcb(d_hour)

        d_min += mn
        if d_min > 59: 
            d_min -= 60
            d_hour += 1

        d_hour += hr
        if d_hour > 23: d_hour -= 24

        d_min = self.chg_bcd(d_min)
        d_hour = self.chg_bcd(d_hour)
        bus.write_byte_data(0x68, 0x0b, d_min)
        bus.write_byte_data(0x68, 0x0c, d_hour)

        dt_now = datetime.datetime.now()               
        time_no += 1
        a = self.get_temp()
        f = open('data.txt', 'a')
        f.write(str(time_no) + "," + dt_now.strftime('%Y年%m月%d日 %H:%M:%S') + ',' + str(f'{a:.1f}') +'\n')
        f.close()

        d_min = bus.read_byte_data(0x68, 0x0f)
        d_min &= 0xfd
        bus.write_byte_data(0x68, 0x0f, d_min)

        print("innterrupt\n")

    #-------------------------------------------------
    def chg_bcd(self,data):
        return(((data // 10) << 4) + ( data % 10))

    #-------------------------------------------------
    def chg_dcb(self,data):
        return((data >> 4) * 10 + ( data & 0xf))

#---------------------------------------------------------

host = ''
port = 8080
httpd = HTTPServer((host, port), MyHandler)
print('serving at port', port)
httpd.serve_forever()

  • 10,11行: subprocess.run()を実行する為に必要なライブラリー
  • 91から104行: ”Mail”ボタンを押すとここが実行されます。ここでメールを送信しています。
    • 93,94行: ”mail.org”ファイルを元に、コピーファイル”mail.txt”を作成。
    • 96から100行:コピーしたファイルに測定時間と温度を追加しています。
    • 102,103行: ここで、送信先に作成したファイルを送信しています。

CSSファイル、JSファイルの変更は有りませんが、とりあえず載せておきます。

http_server_06.css

@charset "UTF-8";

.t_font {
        font-size: 40px;
        font-weight:bold;
        font-style: italic;
        color: #0ff;
}

.b_frame {
        width: 400px;
        background: #363636;
        padding: 15px;
        border-radius: 10px;
        margin-top: -30px;
        margin-right: 10px;
        color: #EFEFEF;
}

button {
        display: block;
        margin: 10px;
        line-height: 30px;
        cursor: pointer;
        color: #fff;
        background: #228b22;
        border-radius: 10px;
        font-size: 24px;
	    width:120px;
}

.inp_box {
	    width:40px;
        font-size: 16px;
	    text-align: right;

}

http_server_06.js

    var url = "http://raspberrypi.local:8080/?80=0";
    var xhr = new XMLHttpRequest();	
    var str;
    var para=['0','0','0','0','0','0'];

    xhr.open('GET', url);
    xhr.send();
     
    xhr.onreadystatechange = function() 
    {
      if(xhr.readyState === 4 && xhr.status === 200) 
      {
        console.log( xhr.responseText );

        b=0;
        for(a = 0; a < 5; a++)
   		{
            para[a]='';
   		    while( xhr.responseText[b] != ',') 
            {
                para[a] += xhr.responseText[b];
   		        b ++;
            }
            b ++; 
        }

        str = "OFF";
        if(Number(para[0])) str = "ON";
        document.getElementsByName('1')[0].innerHTML = str;
        document.getElementById('1').innerHTML = para[1] + "℃";
	    document.getElementById('2').value = Number(para[2]);
	    document.getElementById('3').value = Number(para[3]);

        str = true;
        if(para[4] == "0") str = false;
        else document.getElementsByName('4')[0].style.backgroundColor = "gray";
        document.getElementsByName('4')[0].disabled = str;

      }
    }

    function onBtn_Set_Inter() {
	    var a;
	    var str="";

        str += (document.getElementById(String(2)).value + ",");
        str += document.getElementById(String(3)).value
		document.getElementsByName('4')[0].value=str;
	}

実行

いつもの様に、全てのファイルを同じフォルダーに保存して、”python3 hello_07.py”と実行して下さい。

ブラウザを上げてHPを表示させ、”Mail”ボタンを押して下さい。宛先に下記の様なメールが届きます。下2行に測定時間と温度が表示されています。


to: xxxxx@yyyyy
from: aaaaa@yahoo.co.jp
Subject: 送信テスト

ファイルの送信テストです。
測定時間:2021年03月17日 18:30:16
温度:26.7

メールの送信だけでは寂しいので

HPに、”Radio”ボタンを追加して、ボタンのオンオフでラジオのオンオフとなる様にしています。

変更したのは、HTLMとPythonファイルのみです。

index_07a.html

<!doctype html>
<html>
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width,initial-scale=1">
        <link rel="stylesheet" type="text/css" href="./http_server_06.css" >
        <title>http-server 1.0</title>
    </head>
    <body>
        <center>
            <div class="b_frame">
   	        <div class="t_font"><u>HTTP-Server 1.0</u></div><br>
		        <form method='get'>

                  <div style="display:flex; justify-content:space-evenly;"> 
        	        <button type='submit' name='1' value='0' >On</button>
        	        <button type='submit' name='2' value='0' >Clear</button>
                  </div>

		          <p style="font-size:25px;" id='1'>Tenp:</p>
    		      <button type='submit' name='3' value='0' >Measure</button>

		          <div class="disp-group"> 
		            <p style="font-size:25px;">Interval</p>
			        <div> 
                        <input type='number' max='23' min='0' id='2' class='inp_box' />(H)&nbsp;&nbsp;
			            <input type='number' max='59' min='0' id='3' class='inp_box' />(M) 
                    </div>
		          </div>
                
		          <div style="display:flex; justify-content:space-evenly;"> 
    	              <button type='submit' name='4' value='0' onclick='onBtn_Set_Inter()' >Start</button>
    	              <button type='submit' name='5' value='0' >Stop</button>
		          </div>

		          <a style="font-size:25px;color:#00ffff;cursor: pointer;" href='./data.txt' download='data.txt'>Download00</a>

                  <button type='submit' name='6' value='0' >Mail</button>
                  <button type='submit' name='7' value='0' >Radio</button>

                </form>
            </div>
        </center>
      <script src="http_server_06.js"></script>
    </body>
</html>
  • 39行目: ”Radio”ボタンの追加。Nameは、”7”。
hello_07a.py

from http.server import HTTPServer, SimpleHTTPRequestHandler
from urllib.parse import urlparse,parse_qs

import RPi.GPIO as GPIO

import datetime

import smbus

import subprocess
from subprocess import PIPE

btn_stat = 0
run_stat = 0
radio_stat = 0
time_no = 0
hr = 0
mn = 0

GPIO.setmode(GPIO.BCM)      
GPIO.setup(17, GPIO.OUT)   
GPIO.output(17,0)        
GPIO.setup(4, GPIO.IN, GPIO.PUD_UP) 
    
bus = smbus.SMBus(1)

f = open('data.txt', 'w')
f.close()

proc_pt = subprocess.Popen("clear",shell=True)

class MyHandler(SimpleHTTPRequestHandler):

    def do_GET(self):
        global btn_stat, run_stat, hr, mn, f, proc_pt, radio_stat
            
        self.send_response(200)

        fl = 1
        if self.chk_file() == 0:
            parsed = urlparse(self.path)
            params = parse_qs(parsed.query)
            a=next(iter(params))

        #-----  LED ON/OFF  --------------
            if a == "1":
                if btn_stat == 0:
                    GPIO.output(17,1)
                    print("LED_On\n")
                    btn_stat = 1
                else:
                    GPIO.output(17,0)
                    print("LED_Off\n")
                    btn_stat = 0

        #-----  Port Clear  -----------------
            elif a == "2":
                GPIO.cleanup()  
                print("IO_Clear\n")

        #-----  Measure temp  -----------------
            elif a == "3":
                print("Measurement\n")

        #-----  Start  -----------------
            elif a == "4":
                run_stat = 1 
                b = params['4'][0]
                b = b.split(",")
                hr = int(b[0])
                mn = int(b[1]) 
 
                time_no = 0
                self.DS3231_init()
                
                f = open('data.txt', 'w')
                f.close()

                self.measure_next(123)
                a = bus.read_byte_data(0x68, 0x0e)
                a |= 2
                bus.write_byte_data(0x68, 0x0e, a)
                GPIO.add_event_detect(4, GPIO.FALLING, callback=self.measure_next)

        #-----  Stop   -----------------
            elif a == "5":
                run_stat = 0
                a = bus.read_byte_data(0x68, 0x0e)
                a &= 0xfd
                bus.write_byte_data(0x68, 0x0e, a)
                GPIO.remove_event_detect(4)
                print("Stop\n")

        #-----  Send Mail   -----------------
            elif a == "6":
                cmd="cp -f mail.org mail.txt"
                proc = subprocess.run(cmd,shell=True)

                dt_now = datetime.datetime.now()               
                a = self.get_temp()
                f = open('mail.txt', 'a')
                f.write("測定時間:" + dt_now.strftime('%Y年%m月%d日 %H:%M:%S') + '\n温度:' + str(f'{a:.1f}') +'\n')
                f.close()

                cmd="msmtp fuji.ooba@gmail.com <mail.txt"
                proc = subprocess.run(cmd,shell=True)

                print("Stop\n")


        #-----  Radio   -----------------
            elif a == "7":
                if radio_stat == 1: radio_stat = 0
                else: radio_stat = 1

                if radio_stat == 1:
                    cmd = "mplayer -playlist http://www.simulradio.info/asx/tsukuba.asx"
                    proc_pt =  subprocess.Popen(cmd.split())
                else:
                    proc_pt.kill()


        #-----  Data transfer  --------------
            elif a == "80":
                print("Data transfer\n")
                self.send_header('Content-type', "text/plain")
                self.end_headers()

                a = self.get_temp()
                buf = str(btn_stat) + ',' + str(f'{a:.1f}') + ',' + str(hr) + ',' + str(mn) + ',' + str(run_stat) + ','
                self.wfile.write(buf.encode())

                fl = 0
        #-------------------------------------

        else :
            fl = 0

        if fl == 1:
            f = open("index_07a.html",'rb')
            self.send_header('Content-type', "text/html")
            self.end_headers()
            self.wfile.write(f.read())
            f.close()

    #-------------------------------------------------
    def chk_file(self):

        a=0
        if self.path == "/":
            self.path = "/index_07a.html"
            dataType = "text/html"
            a=1
        if self.path.endswith(".css"):
            dataType = "text/css"
            a=1
    
        if self.path.endswith(".js"):
            dataType = "application/javascript"
            a=1
    
        if self.path.endswith(".txt"):
            dataType = "txt/plain"
            a=1
    
        if a == 1:
            self.path = "." + self.path
            f = open(self.path,'rb')
            self.send_header('Content-type', dataType)
            self.end_headers()
            self.wfile.write(f.read())
            f.close()
        return a

    #-------------------------------------------------
    def get_temp(self):
        global bus

        # 13bit mode
        bus.write_byte_data(0x48, 0x03, 0x00)
        word_data = bus.read_word_data(0x48, 0x00)

        data = (word_data & 0xff00) >> 8 | (word_data & 0xff) << 8
        data /= 128
        return(data) 

    #-------------------------------------------------

    def DS3231_init(self):

        for a in range(3):
            bus.write_byte_data(0x68, a, 0)

        a = bus.read_byte_data(0x68, 0x0d)
        a |= 0x80
        bus.write_byte_data(0x68, 0x0d, a)

    #-------------------------------------------------
    def measure_next(self,dummy):
        global hr, mn, bus, f, time_no

        d_min = bus.read_byte_data(0x68, 0x01)
        d_min = self.chg_dcb(d_min)
        d_hour = bus.read_byte_data(0x68, 0x02)
        d_hour = self.chg_dcb(d_hour)

        d_min += mn
        if d_min > 59: 
            d_min -= 60
            d_hour += 1

        d_hour += hr
        if d_hour > 23: d_hour -= 24

        d_min = self.chg_bcd(d_min)
        d_hour = self.chg_bcd(d_hour)
        bus.write_byte_data(0x68, 0x0b, d_min)
        bus.write_byte_data(0x68, 0x0c, d_hour)

        dt_now = datetime.datetime.now()               
        time_no += 1
        a = self.get_temp()
        f = open('data.txt', 'a')
        f.write(str(time_no) + "," + dt_now.strftime('%Y年%m月%d日 %H:%M:%S') + ',' + str(f'{a:.1f}') +'\n')
        f.close()

        d_min = bus.read_byte_data(0x68, 0x0f)
        d_min &= 0xfd
        bus.write_byte_data(0x68, 0x0f, d_min)

        print("innterrupt\n")

    #-------------------------------------------------
    def chg_bcd(self,data):
        return(((data // 10) << 4) + ( data % 10))

    #-------------------------------------------------
    def chg_dcb(self,data):
        return((data >> 4) * 10 + ( data & 0xf))

#---------------------------------------------------------

host = ''
port = 8080
httpd = HTTPServer((host, port), MyHandler)
print('serving at port', port)
httpd.serve_forever()
  • 111から120行: ここでラジオの処理をしています。

つくばラジオですが、”Radio”ボタンのオンオフでラジオのオンオフが出来ます。もちろんラジオを聞きながら温度の測定、メールの送信も出来ます。

今回はここまで。次回はこのHTTPサーバで出来るその他の事について簡単に紹介します。