最近のUIはWeb越し。しかし、HTMLやJavaScript周りの仕様は、大体が、ユーザが何かをクリックしたらどうこうという、従来のマウス・タッチUIを前提にしています。
一方、端末側のパワーが非力なので(端末にパワーを持たせるとシステムが高価になる)、センサーのデータをサーバで処理したい。そして、サーバ側の処理結果に応じて、UIを制御したい。そこでは、ユーザがクリックやタッチするイベントはありません。
現状のHTML/JavaScriptによるUIを使いながら、サーバ側からの制御を可能にするにはどうするか? asyncio の aiohttp の WebSocketがこの用途に使えます。
以下、クライアントラズパイで、ブラウザとPythonプロセスを動かし、サーバ側のPythonで両者と通信し、サーバの処理に応じて、ブラウザにUI指示を送り、クライアントPythonに UI 以外(センサーなど)の指示を送る、ようなプログラムのスケルトンです。
サーバ
import asyncio
import aiohttp.web
...
async def browser_handler(request):
browser_websocket = aiohttp.web.WebSocketResponse()
await browser_websocket.prepare(request)
# await send_str()でDOM制御指示 ...
return browser_websocket
async def client_handler(request):
clientpy_websocket = aiohttp.web.WebSocketResponse()
await clientpy_websocket.prepare(request)
# await send_str()やawait receive_str()で、
# クライアントのデータやセンサー制御指示のやり取り...
return clientpy_websocket
if __name__ == '__main__':
loop = asyncio.get_event_loop()
app = aiohttp.web.Application(loop=loop)
app.router.add_route('GET', '/ws/b', browser_handler)
app.router.add_route('GET', '/ws/c', client_handler)
aiohttp.web.run_app(app, host=HOST, port=PORT)
クライアントPython
import asyncio
import aiohttp
...
async def client():
session = aiohttp.ClientSession()
async with session.ws_connect('http://HOST:PORT/ws/c') as ws:
# await send_str()したりawait receive_str()したりして,
# サーバから指示を受け、クライアントラズパイにつけたセンサーを
# 操作したり、別ポートでデータストリームをUPD送信するように設定
# したりする処理...
if __name__ == '__main__':
try:
loop = asyncio.get_event_loop()
loop.run_until_complete(client())
finally:
loop.close()
クライアントブラウザ
<!DOCTYPE html>
<html><head>
</head>
<body>
<script>
var ws = new WebSocket('http://HOST:PORT/ws/b');
ws.onmessage = function(e) {
// サーバからの指示に応じてDOM操作
};
...