節約プログラマー雑記

Djangoとaxiosで赤外線リモコン

以前、Raspberry Piで赤外線を扱うための基盤を紹介しました。ですがそのままだと、指示を出すのがコマンド形式となっているため、日常ではまだまだ使いにくいかと思います。
そこで今回、画面からRaspberry Piに指示を出すためのWeb画面をDjangoとaxiosを使って作ってみましたので、それを紹介していきたいと思います。

1. 概要

基本的には、DjangoのMVTの構成に則って構築をしており、サーバーサイド側としては、view.pyに通信された内容に従って、赤外線通信を起動することを実装しています。ただ、画面遷移を考えるのが面倒だったため、冒頭に記載したように、通信をformによるPOST処理ではなく、axiosを使ってJavascriptで通信する構成となっています。

2. Web画面(Template部分)

Web画面となるTemplateは下記のようになります。

Web画面(Template部分)

・・・(前略)・・・
      <div class="col-md-8">
        Air_Con_Contorol:
        <a class="btn btn-primary" href="javascript:contrlAirCon('on');">ON!!</a>
        <a class="btn btn-secondary" href="javascript:contrlAirCon('off');">OFF</a>
      </div>
・・・(中略)・・・
 {% csrf_token %}
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.bundle.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.19.2/axios.js"></script>
<script>

//通信処理
async function contrlAirCon(param){
   var msg = '';
   var params = new URLSearchParams();
   params.append('ctrl',param);
   headers = {'X-CSRFToken': csrftoken};
   await axios.post("{% url 'api:module_con' %}",
     params,
     {headers:headers}
   )
   .then(response => {
     msg = response.data.msg;
   });
   alert(msg);
}

//CSRFトークン取得処理
function getCookie(name){
  var cookieValue = null;
  if (document.cookie && document.cookie !== ''){
    var cookies = document.cookie.split(';');
    for (var i= 0; i < cookies.length; i++){
      var cookie = cookies[i].trim();
      if (cookie.substring(0, name.length + 1)===(name + '=' )) {
        cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
        break;
      }
    }
  }
  return cookieValue;
}
</script>

ポイントとしては、aタグにてボタンを用意し、hrefの参照先をJavascriptの送信メソッドとすることで、ボタンがクリックされたタイミングでaxios(contrlAirCon関数部分)に引数を渡して送信するようになっています。

その際、POSTで処理する際の注意点として、DjangoはCSRF対策がデフォルトで行っているため、HTTPのヘッダに「X-CSRFToken」が設定されていない場合、403エラーとなってしまいます。そこでテンプレートのどこでもよいので、 {% csrf_token %}を記載して、クライアント側にCSRFトークンの値を渡し、クライアント側は上記のgetCookie関数のようにトークンを取得、ヘッダーに設定して送信をすることが必要です。

(setting.pyにて、'django.middleware.csrf.CsrfViewMiddleware'を削除して、CSRF対策を止める方法もありますが、これはDjangoとしては非推奨となっているようです。)

実際にソースが出来上がると下の「Aircon」、「Light」の部分のようにボタンが作成され、ボタンを押すとサーバー側から処理結果のメッセージが返却されて、アラートで処理結果のメッセージが表示されるようになります。

web_image1.png

3. ロジック部分(View部分)

クライアントが出来上がったら、次はサーバーサイドの仕組みになります。 サーバーサイドのaxiosの通信先のロジックが次のようになっています。

ファイル名:view.py

from django.views import View
from django.http.response import JsonResponse
from datetime import datetime, timedelta,date
import subprocess

class ModuleConApi(View):

    def post(self,request,*args, **kwargs):
        ret = {}
        msg = ''
        cmd = None
        param = request.POST.get(key="ctrl",default=None)
        if param == 'on':
            cmd = "/home/pi/scripts/sh/mod_con.sh on"
            msg = " AirCon is ON"
        elif param == 'off':
            cmd = "/home/pi/scripts/sh/mod_con.sh off"
            msg = " AirCon is OFF"
        elif param == 'light:switch':
            cmd = "/home/pi/scripts/sh/mod_con.sh light:switch"
            msg = " Light Switch"
        elif param == 'light:night':
            cmd = "/home/pi/scripts/sh/mod_con.sh light:night"
            msg = " Night Ligtht"
        else:
            msg = "Do Nothing"

        if cmd is not None:
            subprocess.call(cmd.split())

        ret.update(msg=msg)
        return JsonResponse(ret)

ポイントとしては、Ajax通信のため、JsonResponseをimportして、「JSON形式で通信を返却している点」、また、クライアントからの引数を基に「shellで赤外線のスクリプトを実行している点」になります。それ以外は取り立てて、普通のDjangoの記載と比べて変わった処理はありません。

また、view.pyから呼び出されているshellが次のようになっています。

ファイル名:mod_con.sh

#!/bin/bash
param=$1
if [ $param == 'on' ]; then
   python /home/pi/scripts/python/irrp.py -p -g26 -f /home/pi/scripts/params/codes air_con:dry
elif [ $param == 'off' ]; then
   python /home/pi/scripts/python/irrp.py -p -g26 -f /home/pi/scripts/params/codes air_con:off
elif [ $param == 'light:switch' ]; then
   python /home/pi/scripts/python/irrp.py -p -g26 -f /home/pi/scripts/params/codes light:switch
elif [ $param == 'light:night' ]; then
   python /home/pi/scripts/python/irrp.py -p -g26 -f /home/pi/scripts/params/codes light:night
fi
echo "$date"":""$param" >> ~/scripts/sh/chk.log

exit 0;

ここまでできたら、後はurls.pyに作成したviewを登録すれば、サーバーサイドも準備完了です。

ファイル名:urls.py

from django.conf.urls import url
from django.urls import path

from api.views import(
        ModuleConApi,
)

app_name = 'api'

urlpatterns = [
        path('air_con',ModuleConApi.as_view(),name='module_con'),
]

可能であれば、直接pythonのモジュールとして読み込んで「view.py」の中で起動できるのが良かったのですが、「irrp.py」の内容を修正するのが面倒だったため、一枚shellを噛ませてpythonを起動するような形で対応しました。

4. 動作確認・後書き

以上がRaspberry Piを利用したWebの赤外線リモコンの作成になります。実際に出来上がった画面から、エアコンをONにすると、アラートでメッセージが表示され、エアコンを点けることに成功しました。

・Web画面 web_image2.png
・エアコンの様子 aircon.jpg

まあ実際に作ってみると、画期的に便利になるということはないですが、家の中のどこからでもエアコンや電灯をスマホ一つでいじれるようになり、ちょっと便利かなーとは思います。
もしよかったら、ぜひお試しください。