This article can be understood by anyone who has used Flask and has written a socket communication program. Let's try controlling home appliances using Flask of Python. Node.js is often used to control home appliances, but since the number of people using Python is increasing, I will use Python to control home appliances. First, as the communication protocol, ECHONET Lite, which is used as standard when controlling home appliances, is used. Not many home appliances are compatible with ECHONET Lite. However, even if it cannot be controlled by ECHONET Lite, it may be possible to control it from there if WebAPI etc. is provided. When finding a home appliance to control, try searching for WebAPI as well as ECHONET Lite. Next, regarding the home appliances to be controlled this time, it is difficult to prepare ECHONET LITE compatible home appliances, so we will use an emulator instead.
The development environment is as follows. This time we will use two PCs. --PC for running the emulator - Windows 10 (64bit) --Java installed
--PC for executing home appliance control program - Python 3.8.2 - Ubuntu 18.04.5 LTS
The emulator uses MoekadenRoom, so go to GitHub page and go to README.md Download the compressed file from
Download executables of
. Extract the compressed file and execute MoekadenRoom.exe
. To execute it, Java must be installed. If Java is not installed, please install it from here. When you execute MoekadenRoom.exe
, the emulator will be displayed as shown below. You can operate home appliances with GUI, so please try it.
Send control commands to home appliances with Python. Since it is almost the same as a general socket communication client program, the sample code is shown first. If you execute the sample code while MoekadenRoom is running, the emulator lights will turn on.
echonet_lite.py
import socket
import binascii
def send_command():
#IP address of the PC running Moekaden Room (change each one)
ip = '192.168.0.10'
#ECHONET Lite uses UDP port 3610
ECHONETport = 3610
format_echonet_lite = ['EHD', 'TID', 'SEOJ', 'DEOJ', 'ESV', 'OPC', 'EPC', 'PDC', 'EDT']
data_format = {
format_echonet_lite[0]: '1081',
format_echonet_lite[1]: '0000',
format_echonet_lite[2]: '05FF01',
format_echonet_lite[3]: '029001',
format_echonet_lite[4]: '60',
format_echonet_lite[5]: '01',
format_echonet_lite[6]: '80',
format_echonet_lite[7]: '01',
format_echonet_lite[8]: '30' #You can turn off the lights by changing to 31
}
frame = ''
for key in format_echonet_lite:
frame += data_format[key]
#Convert a hexadecimal string to a byte string
msg = binascii.unhexlify(frame)
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.sendto(msg, (ip, ECHONETport))
if __name__ == '__main__':
send_command();
This is an explanation of the sample code. Creates a message to be forwarded based on the format of the ECHONET Lite frame. Please refer to the following page for the explanation about the frame. -How to create a telegram for ECHONET Lite -ECHONET Lite Standard Chapter 3 Telegram Structure
Refer to the above page and enter values in ʻEHD, TID, SEOJ, DEOJ, ESV, OPC, EPC, PDC, EDT`. Please check the above page for details. The value is a hexadecimal number.
Telegram component | Value (description) |
---|---|
ETD | 1081 (ECHONET protocol type, 1081 is ECHONET Lite) |
TID | 0000 (An ID for associating a request and a response, which can be any numerical value.) |
SEOJ | 05FF01 (Indicates the source device. The value of the controller is entered. reference:Detailed regulations for device objects) |
DEOJ | 029001 (The device (lighting) of the destination is shown. I put the value of lighting) |
ESV | 60 (60 is a control command with no response from the destination) |
OPC | 01 (Shows the number of properties to process in one telegram) |
EPC | 80 (Specify the property.MoekadenRoomReadme.SearchforLightningEPCfrommd) |
PDC | 01 (Indicates the number of bytes in EDT. 01 because there is only one control content) |
EDT | 30 (Enter the value according to the proper name specified by EPC.MoekadenRoomReadme.SearchforEDTtoturnonthelightfrommd) |
The previous control command was unresponsive. Here, we will send a control command and receive a response from the home appliance. As before, turn on the emulator lamp. The difference is that you receive a response. In other words, we created only the socket communication client program earlier, but here we also need to create the socket communication server program in order to receive the response. We will explain two methods, one is to separate the files for the client program and the server program and execute them separately, and the other is to combine them into one file. First, I will explain from the simple former.
The client program is almost the same as before. It is the ECHONET Lite frame value that is changed. By setting the value of ʻESV to
61`, you can send a control command with a response.
echonet_lite.py
import socket
import binascii
def send_command():
#IP address of the PC running Moekaden Room (change each one)
ip = '192.168.0.10'
#ECHONET Lite uses UDP port 3610
ECHONETport = 3610
format_echonet_lite = ['EHD', 'TID', 'SEOJ', 'DEOJ', 'ESV', 'OPC', 'EPC', 'PDC', 'EDT']
data_format = {
format_echonet_lite[0]: '1081',
format_echonet_lite[1]: '0000',
format_echonet_lite[2]: '05FF01',
format_echonet_lite[3]: '029001',
format_echonet_lite[4]: '61', #Change the previous program (60 → 61)
format_echonet_lite[5]: '01',
format_echonet_lite[6]: '80',
format_echonet_lite[7]: '01',
format_echonet_lite[8]: '30'
}
frame = ''
for key in format_echonet_lite:
frame += data_format[key]
#Convert a hexadecimal string to a byte string
msg = binascii.unhexlify(frame)
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.sendto(msg, (ip, ECHONETport))
if __name__ == '__main__':
send_command();
The program that receives the response is no different from a general socket communication server program. However, since the response is received as a byte string, it must be converted to a hexadecimal string. Since the data received at the time of response is in the same format as the frame sent earlier, the contents can be read by converting it to a hexadecimal character string.
socket_server.py
import socket
import binascii
def receive_state():
ECHONETport = 3610
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind(('', ECHONETport))
data, addr = sock.recvfrom(4096)
#Convert a byte string to a hexadecimal string
data = str(binascii.hexlify(data), 'utf-8')
print(data)
if __name__ == '__main__':
receive_state()
First, start MoekadenRoom and execute the server program and then the client program in that order. As a result of executing the server program, the following output will be obtained.
$ python3 socket_server.py
#Output result
1081000002900105ff0171018000
When the output result is read, it will be as follows. From the value of ʻEPC`, we can see that it is a response to the control command.
Telegram component | Value (description) |
---|---|
ETD | 1081 (ECHONET protocol type, 1081 is ECHONET Lite) |
TID | 0000 (An ID for associating a request and a response, which can be any numerical value.) |
SEOJ | 029001 (Source device: Lighting) |
DEOJ | 05FF01 (Destination device: Controller) |
ESV | 71 (The response to 61 is returned with 71) |
OPC | 01 (Number of properties to process in one telegram) |
EPC | 80 (Property specification. Lighting EPC) |
PDC | 00 (Number of bytes in EDT. 00 because it is not control) |
EDT | Sky(Do not specify EDT because it is not a control) |
In the server program, it waits until it receives the data (blocking), so the processing stops at that part. To process the client program at the same time, it is better to use thread
. The server program is processed in a separate thread.
To pass the process to another thread, create an instance of the Thread
class and specify the function you want to process in the other thread in the argument target
. Start thread
with the start
method.
th = Thread(target=function)
th.start()
Now, here is the sample code. It is a program that sends a control command to the lighting and returns a response 3 seconds after the program is executed.
echonet_lite.py
import socket
import binascii
from threading import Thread
import time
def send_command():
#IP address of the PC running Moekaden Room (change each one)
ip = '192.168.0.10'
ECHONETport = 3610
format_echonet_lite = ['EHD', 'TID', 'SEOJ', 'DEOJ', 'ESV', 'OPC', 'EPC', 'PDC', 'EDT']
data_format = {
format_echonet_lite[0]: '1081',
format_echonet_lite[1]: '0000',
format_echonet_lite[2]: '05FF01',
format_echonet_lite[3]: '029001',
format_echonet_lite[4]: '61',
format_echonet_lite[5]: '01',
format_echonet_lite[6]: '80',
format_echonet_lite[7]: '01',
format_echonet_lite[8]: '30', #You can turn off the lights by changing to 31
}
frame = ''
for key in format_echonet_lite:
frame += data_format[key]
msg = binascii.unhexlify(frame)
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.sendto(msg, (ip, ECHONETport))
print('send')
def receive_state():
ECHONETport = 3610
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind(('', ECHONETport))
data, addr = sock.recvfrom(4096)
data = str(binascii.hexlify(data), 'utf-8')
print('receive')
print(data)
if __name__ == '__main__':
th = Thread(target=receive_state)
th.start()
time.sleep(3)
send_command()
As a control of home appliances, I will try to acquire the current state of home appliances. In addition to the control commands (with or without response) explained above, the status requests explained here are the main types of home appliance control. Since there is also a response to the status request, it is necessary to create both a client program and a server program for socket communication. In the status request, the value of ʻESVat the time of transmission is
62, and the value of ʻESV
at the time of response is 72
. Also, since it is not a control command, the value of PDC
is 00
and the value of ʻEDT is
empty`. Based on these, the sample code is shown below. In the sample code, the response is output every 5 seconds. While executing the sample code, click the lighting button of Moekaden Room to switch it ON / OFF. The output EDT value should change.
echonet_lite.py
import socket
import binascii
from threading import Thread
import time
def receive_state():
ECHONETport = 3610
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind(('', ECHONETport))
while True:
data, addr = sock.recvfrom(4096)
data = str(binascii.hexlify(data), 'utf-8')
print(data)
def confirm_state():
ip = '192.168.0.10'
ECHONETport = 3610
format_echonet_lite = ['EHD', 'TID', 'SEOJ', 'DEOJ', 'ESV', 'OPC', 'EPC', 'PDC', 'EDT']
data_format = {
format_echonet_lite[0]: '1081',
format_echonet_lite[1]: '0000',
format_echonet_lite[2]: '05FF01',
format_echonet_lite[3]: '029001',
format_echonet_lite[4]: '62',
format_echonet_lite[5]: '01',
format_echonet_lite[6]: '80',
format_echonet_lite[7]: '00',
format_echonet_lite[8]: ''
}
frame = ''
for key in format_echonet_lite:
frame += data_format[key]
msg = binascii.unhexlify(frame)
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
while True:
sock.sendto(msg, (ip, ECHONETport))
time.sleep(5)
if __name__ == '__main__':
th1 = Thread(target=receive_state)
th1.start()
confirm_state()
Finally, use Flask to set up an HTTP server to control home appliances. Managing the status of home appliances with an HTTP server is a method often used when controlling multiple home appliances. By applying this, it can be used to control home appliances from Android and also to collect IoT data. First, let's take a look at a simple program that controls Flask and home appliances as a step toward that end. Here, queue
is used because multiple threads are used. Queue
is a structure that can retrieve data in the order in which it was entered. The basic usage of queue
is as follows.
#Creating a queue
q = Queue();
#Insert data into the queue with "put" and retrieve it with "get"
q_msg = {"name": "foge"}
q.put(q_msg)
q_result = q.get()
print(q_result["name"])
Now, it's a little complicated, but here's a sample code. To execute the program, first start MoekadenRoom, then execute the program, and finally access http://192.168.0.10:5000
. It is a program that acquires the status of home appliances every 5 seconds and outputs them, and gives ON / OFF commands to home appliances every 10 seconds.
app.py
import socket
import binascii
from flask import Flask
from queue import Queue
from threading import Thread
import time
app = Flask(__name__)
q = Queue()
state_q = Queue()
@app.route('/', methods=['GET'])
def home():
th1 = Thread(target=receive_state)
th1.start()
th2 = Thread(target=confirm_state)
th2.start()
th3 = Thread(target=send_command)
th3.start()
return 'This is a Sample Webpage'
def send_command():
while True:
time.sleep(10)
if state_q.empty():
state = '30'
else:
q_state_msg = state_q.get()
q_state = q_state_msg['state']
if q_state == '30':
state = '31'
else:
state = '30'
#IP address of the PC running Moekaden Room (change each one)
ip = '192.168.0.10'
ECHONETport = 3610
format_echonet_lite = ['EHD', 'TID', 'SEOJ', 'DEOJ', 'ESV', 'OPC', 'EPC', 'PDC', 'EDT']
data_format = {
format_echonet_lite[0]: '1081',
format_echonet_lite[1]: '0000',
format_echonet_lite[2]: '05FF01',
format_echonet_lite[3]: '029001',
format_echonet_lite[4]: '60',
format_echonet_lite[5]: '01',
format_echonet_lite[6]: '80',
format_echonet_lite[7]: '01',
format_echonet_lite[8]: state # state: 30 or 31 (ON or OFF)
}
frame = ''
for key in format_echonet_lite:
frame += data_format[key]
msg = binascii.unhexlify(frame)
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.sendto(msg, (ip, ECHONETport))
print('send_command')
def receive_state():
ECHONETport = 3610
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind(('', ECHONETport))
while True:
data, addr = sock.recvfrom(4096)
data = str(binascii.hexlify(data), 'utf-8')
queue_msg = q.get()
if state_q.empty():
data_format = queue_msg['format']
state = get_state(data, data_format)
q_state_msg = {
'state': state
}
state_q.put(q_state_msg)
print(data)
def confirm_state():
ip = '192.168.0.10'
ECHONETport = 3610
format_echonet_lite = ['EHD', 'TID', 'SEOJ', 'DEOJ', 'ESV', 'OPC', 'EPC', 'PDC', 'EDT']
data_format = {
format_echonet_lite[0]: '1081',
format_echonet_lite[1]: '0000',
format_echonet_lite[2]: '05FF01',
format_echonet_lite[3]: '029001',
format_echonet_lite[4]: '62',
format_echonet_lite[5]: '01',
format_echonet_lite[6]: '80',
format_echonet_lite[7]: '00',
format_echonet_lite[8]: ''
}
frame = ''
for key in format_echonet_lite:
frame += data_format[key]
msg = binascii.unhexlify(frame)
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
while True:
sock.sendto(msg, (ip, ECHONETport))
print('send_confirm_state')
queue_msg = {
'format': data_format
}
q.put(queue_msg)
time.sleep(5)
#A function to get the EDT value from a frame of a hexadecimal string
def get_state(data, data_format):
data_list = []
data_pos = 0
for value in data_format.values():
element_size = len(value)
if element_size == 0:
element_size = 2
data_list.append(data[data_pos: data_pos + element_size])
data_pos += element_size
state = data_list[8]
return state
if __name__ == '__main__':
app.run(host='0.0.0.0', debug=True)
I used ECHONET Lite in Python to control home appliances (emulators). This time, we focused on the simplest controllable lighting. Based on this sample code, try to realize various controls for home appliances. Since there is little information on controlling home appliances using Python, let's gradually increase the articles on Qiita. (Until you find the most suitable interface to control your home appliances ...)