This is a continuation of Last time. This is what I want to do for the time being.
This time, finally, "Create a Python script for Wake on LAN".
-[1] Obtain a domain using GCP and MyDNS -[2] Set up Softether Server on GCP (Connect from iPhone / Raspberry Pi) -[3] Reverse proxy from Apache on GCP to local Raspberry Apache -[4] Make Raspberry Pi a router -[5] Create a Python script for Wake on LAN.
Assumption: Ubuntu 18.04. First, set up so that Wake on LAN can be executed.
sudo vi /etc/netplan/~~.yaml
Fill in the following,
/etc/netplan/~~.yaml
# Let NetworkManager manage all devices on this system
network:
version: 2
renderer: NetworkManager
ethernets:
eno1:
dhcp4: no
wakeonlan: true # enable wake on lan
addresses: [192.168.1.**/24] # assign arbitrary address
gateway4: 192.168.1.1 # your router(raspi)'s address
nameservers:
addresses: [8.8.8.8,8.8.4.4] # google's public dns
Restart.
sudo netplan apply
sudo reboot
Install ʻethtool` to make sure wake on lan is enabled.
sudo apt-get install -y ethtool
Make a note of the Ethernet name. Next, check if the IP is fixed.
$ ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eno1<<<<copy<<<<: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether **:**:**:**:**:** brd ff:ff:ff:ff:ff:ff
inet 192.168.1.**/24 brd 192.168.1.255 scope global noprefixroute eno1
valid_lft forever preferred_lft forever
inet6 fe80::2d8:61ff:fe56:242d/64 scope link
valid_lft forever preferred_lft forever
Make sure Wake on LAN is enabled. It is OK if it is Wake-on: g
.
$ sudo ethtool eno1
~~
Supports Wake-on: pumbg
Wake-on: g # if it's d, your wake on lan setting may be wrong
~~
Omit. WOL setting (1/3) of the booted PC: BIOS / UEFI setting wo please refer.
Check ʻip a on Raspberry Pi and it is OK if ip is assigned to the Ethernet connected to Desktop. (It must be ʻUP, LOWER_UP
. Thank you telcomM # 588883)))
$ ip a
4: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether **:**:**:**:**:** brd ff:ff:ff:ff:ff:ff
inet 192.168.1.1/24 brd 192.168.1.255 scope global noprefixroute eth1
valid_lft forever preferred_lft forever
inet6 fe80::83fa:6dee:9799:9a6e/64 scope link
valid_lft forever preferred_lft forever
Install wake onlan on Raspberry Pi.
sudo apt install wakeonlan
wakeonlan -i 192.168.1.255 -p 7 **:**:**:**:**:**
If you can start it with this, it's OK.
mod_wsgi
First, install mod_wsgi
and various things that can run Python scripts with Apache.
sudo apt install python3-dev python3-pip apache2 libapache2-mod-wsgi-py3
sudo apt-get -y install python3 ipython3 python3-flask curl
For the time being, create a working Python script. This time, Flask is used.
cd ~
mkdir flask && cd flask
vi app.py
~/flask/app.py
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'This is vpn server for wake on lan!\n\n'
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
For the time being, check if mod_wsgi works.
$ python3 app.py
$ curl http://localhost:5000 #In another tab
This is vpn server for wake on lan!
If you see "This is vpn server for wake on lan!", It's OK.
Create a new configuration file for flask.
sudo vi /etc/apache2/sites-available/flask.conf
This time, I am redirecting from GCP to port 80, so enter as follows.
/etc/apache2/sites-available/flask.conf
<VirtualHost *:80>
ServerName kado.example.com
WSGIDaemonProcess flask user={username} group={username} threads=5
WSGIScriptAlias / /home/{username}/flask/adapter.wsgi
<Directory /home/{username}/flask/>
WSGIProcessGroup flask
WSGIApplicationGroup %{GLOBAL}
WSGIScriptReloading On
Require all granted
</Directory>
</VirtualHost>
Enable flask.conf
.
sudo a2dissite 000-default.conf
sudo a2ensite flask.conf
sudo service apache2 restart
Create an adapter.wsgi file.
vi ~/flask/adapter.wsgi
~/flask/adapter.wsgi
import sys
if sys.version_info[0]<3: # require python3
raise Exception("Python3 required! Current (wrong) version: '%s'" % sys.version_info)
sys.path.insert(0, '/home/kadorpi/flask/')
from app import app as application # <- app means app of app.py
Restart Apache.
sudo service apache2 restart
Check if it works.
$ curl localhost
This is vpn server for wake on lan!
The Python script is now running on Apache.
It will be a little later when we reach here. Send an HTTP request from Line to GCP and redirect the received request to port 80 of Raspberry Pi. Then, the request received by Raspberry Pi is processed by Python.
First, register an account in Line Developer. Next, create a Channel.
Issue a Channel Secret and make a note of it.
Issue a Channel Access Token and make a note of it.
Set Auto-reply etc. from Response settings as appropriate.
Enter the Webhook URL from the Message API. This time, it is
https: // {domain name} / wake-on-lan
.
This completes the LINE Bot settings.
Set the Channel Secret and Channel Access Token that you noted earlier as environment variables.
vi ~/flask/adapter.wsgi
Add the following.
~/flask/adapter.wsgi
import os
os.environ['LINE_CHANNEL_SECRET'] = 'fuga'
os.environ['LINE_CHANNEL_ACCESS_TOKEN'] = 'hogehoge'
Install the required modules.
pip3 install line-bot-sdk flask
The python code looks like this:
python
import os, sys
from flask import Flask, request, abort
app = Flask(__name__)
from linebot import (
LineBotApi, WebhookHandler
)
from linebot.exceptions import (
InvalidSignatureError
)
from linebot.models import (
TextMessage, TextSendMessage, MessageEvent
)
# get channel_secret and channel_access_token from your environment variable
channel_secret = os.getenv('LINE_CHANNEL_SECRET', None)
channel_access_token = os.getenv('LINE_CHANNEL_ACCESS_TOKEN', None)
if channel_secret is None:
print('Specify LINE_CHANNEL_SECRET as environment variable.')
sys.exit(1)
if channel_access_token is None:
print('Specify LINE_CHANNEL_ACCESS_TOKEN as environment variable.')
sys.exit(1)
line_bot_api = LineBotApi(channel_access_token)
handler = WebhookHandler(channel_secret)
@app.route('/')
def show_web():
return 'This is vpn server for wake on lan!\n\n'
@app.route('/wake-on-lan', methods=['POST'])
def wake_on_lan():
# get X-Line-Signature header value
signature = request.headers['X-Line-Signature']
# get request body as text
body = request.get_data(as_text=True)
app.logger.info("Request body: " + body)
# handle webhook body
try:
handler.handle(body, signature)
except InvalidSignatureError:
abort(400)
return 'ok'
@handler.add(MessageEvent, message=TextMessage)
def handle_message(event):
message = event.message.text
replies = []
if re.compile("\s*(check)\s*", re.IGNORECASE).search(message):
result = confirm()
replies += [TextSendMessage(result)]
elif re.compile("\s*(kick)\s*", re.IGNORECASE).search(message):
if confirm() == 'Awake':
replies += [TextSendMessage('Already awake')]
else:
result = kick()
replies += [TextSendMessage(result)]
buttons_template = ButtonsTemplate(
title='usage', text='Tap below buttons', actions=[
MessageAction(label=m, text=m) for m in message_list
])
template_message = TemplateSendMessage(alt_text='Usage', template=buttons_template)
replies += [template_message]
line_bot_api.reply_message(event.reply_token, replies)
def confirm():
hostname = "192.168.1.**"
response = os.system("ping -c 1 " + hostname)
# and then check the response...
if response == 0:
pingstatus = "Awake"
else:
pingstatus = "Sleeping"
return pingstatus
def kick():
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
s.sendto(b'\xFF' * 6 + b'\x**\x**\x**\x**\x**\x**' * 16, ('192.168.1.255', 7))
s.close()
return 'kicked'
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
To give a little explanation, first load the Channel Secret and Channel Access Token for LINE below.
# get channel_secret and channel_access_token from your environment variable
channel_secret = os.getenv('LINE_CHANNEL_SECRET', None)
channel_access_token = os.getenv('LINE_CHANNEL_ACCESS_TOKEN', None)
if channel_secret is None:
print('Specify LINE_CHANNEL_SECRET as environment variable.')
sys.exit(1)
if channel_access_token is None:
print('Specify LINE_CHANNEL_ACCESS_TOKEN as environment variable.')
sys.exit(1)
line_bot_api = LineBotApi(channel_access_token)
handler = WebhookHandler(channel_secret)
The following has been added for Web display.
@app.route('/')
def show_web():
return 'This is vpn server for wake on lan!\n\n'
Write the Webhook request processing set above here.
@app.route('/wake-on-lan', methods=['POST'])
def wake_on_lan():
# get X-Line-Signature header value
signature = request.headers['X-Line-Signature']
# get request body as text
body = request.get_data(as_text=True)
app.logger.info("Request body: " + body)
# handle webhook body
try:
handler.handle(body, signature)
except InvalidSignatureError:
abort(400)
return 'ok'
And finally, when "kick" comes in the message, send MagicPacket, and when "check" comes, I write the process to check if it can be started by Ping.
@handler.add(MessageEvent, message=TextMessage)
def handle_message(event):
message = event.message.text
replies = []
if re.compile("\s*(check)\s*", re.IGNORECASE).search(message):
result = confirm()
replies += [TextSendMessage(result)]
elif re.compile("\s*(kick)\s*", re.IGNORECASE).search(message):
if confirm() == 'Awake':
replies += [TextSendMessage('Already awake')]
else:
result = kick()
replies += [TextSendMessage(result)]
buttons_template = ButtonsTemplate(
title='usage', text='Tap below buttons', actions=[
MessageAction(label=m, text=m) for m in message_list
])
template_message = TemplateSendMessage(alt_text='Usage', template=buttons_template)
replies += [template_message]
line_bot_api.reply_message(event.reply_token, replies)
def confirm():
hostname = "192.168.1.**"
response = os.system("ping -c 1 " + hostname)
# and then check the response...
if response == 0:
pingstatus = "Awake"
else:
pingstatus = "Sleeping"
return pingstatus
def kick():
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
s.sendto(b'\xFF' * 6 + b'\x**\x**\x**\x**\x**\x**' * 16, ('192.168.1.255', 7))
s.close()
return 'kicked'
Restart Apache.
sudo service apache2 restart
In the second half, I was tired and drowned. .. .. Furthermore, it may be difficult to understand because it is written as a memorandum. .. .. I'm glad I could write everything for the time being. I'm glad if you can use it as a reference.
How To Use Apache HTTP Server As Reverse-Proxy Using mod_proxy Extension
Recommended Posts