Build a lightweight server in Python and listen for Scratch 2 HTTP extensions

Why is there a lightweight HTTP server?

--Scratch 2 offline version allows you to load additional blocks from a JSON-formatted file. --The block makes a GET request to an HTTP server outside of Scratch. --You can extend the functionality of Scratch by launching a helper app that listens for GET. ――However, it seems that the overhead is large to generate threads for each request that arrives in large numbers from Scratch loops. --So, let's try to process GET requests in parallel with a single thread.

As an example of the explanation here, the method of using speech synthesis and speech recognition in the Scratch 2 offline version is summarized below. -Scratch 2.0 Offline Voice Dialogue Robot / Block for Voice Dialogue System

Also, put the code that appears in the description below on github.

Add blocks in Scratch 2 s2e file

Explanation PDF In Scratch Extension Page If you read HTTP-9-11.pdf), it says that you can add blocks by creating a JSON format file with the extension .s2e and reading it. (It doesn't matter what the extension is, but it seems that you should use .s2e.) Below, I will try using the example given in this explanation.

test.s2e


{ "extensionName": "Extension Example",
    "extensionPort": 12345,
    "blockSpecs": [
        [" ", "beep", "playBeep"],
        [" ", "set beep volume to %n", "setVolume", 5],
        ["r", "beep volume", "volume"],
    ]
}

Save this in a file called test.s2e. If you hold down Shift and select [File] in Scratch, [Load experimental HTTP extension] will be displayed, so if you select and load test.s2e saved there, it will certainly block. Will be added!

extension_example_empty-s.png

that? The circle next to ʻExtension Example` is red.

According to the Explanation PDF, Scratch requests polling about 30 times per second GET / poll I'm sending to the helper, but it seems to turn red if there is no response to it. If there is a reaction from the helper, it will turn green.

Lightweight HTTP server in Python

Part 1: Responds to GET / poll

--First, try to return something to GET / poll. --In this example, it seems to communicate with port 12345, so let's create an HTTP server that listens on port 12345. --You can easily create a lightweight HTTP server by using aiohttp, which is based on the asynchronous I / O library asyncio. --Since Python 3.5, you can use familiar modifiers in C # such as async and await. The following assumes Python 3.5.

Install aiohttp with $ pip install aiohttp and create the following program (testhelper.py).

testhelper.py


from aiohttp import web

async def handle_poll(request):
    return web.Response(text="OK")

app = web.Application()
app.router.add_get('/poll', handle_poll)

web.run_app(app, host='127.0.0.1', port=12345)

I will try it.

> python testhelper.py
======== Running on http://127.0.0.1:12345 ========
(Press CTRL+C to quit)

By the way, when I look at the Scratch screen ... Oh, it's green!

extension_example_polling-s.png

Just in case, try GET from bash with nc.

$ nc localhost 12345
GET /poll HTTP/1.1

If you press return with this ...

HTTP/1.1 200 OK
Content-Type: text/plain; charset=utf-8
Content-Length: 2
Date: Tue, 19 Sep 2017 13:41:21 GMT
Server: Python/3.5 aiohttp/2.1.0

OK

It seems that aiohttp is replying.

Part 2: Send commands from Scratch (command block)

First, let's implement the beep block as a review. The block that sends commands from Scratch to the helper is called a "command block". If you look at s2e, you can see that this is associated with the command name playBeep. At this time, GET / playBeep is sent to the helper, and the helper should take this and do something. Try ringing the bell with \ 007.

async def handle_beep(request):
    print("play beep!")
    print("\007")
    return web.Response(text="OK")

#abridgement

app.router.add_get('/playBeep', handle_beep)

web.run_app(app, host='127.0.0.1', port=12345)

If you press the beep block in Scratch 2, you will certainly hear a sound.

Next, try implementing the command block set beep volume to (...) that sends the value. In the following example, vol has a value, so you can retrieve it withrequest.match_info ['vol'].

async def handle_setvolume(request):
    volume = int(request.match_info['vol'])
    if volume >= 0 and volume <= 10:
        
        print("set volume= " + str(volume))
    else:
        print("out of range: " + str(volume))
    return web.Response(text="OK")

#abridgement

app.router.add_get('/setVolume/{vol}', handle_setvolume)

web.run_app(app, host='127.0.0.1', port=12345)

The display changes depending on whether you enter a value from 0 to 10 and press the block, or if you enter any other value. (It is also necessary to check ʻisinteger () before ʻint (), but omit it.)

Part 3: Receive value in Scratch (reporter block)

Finally, let's return the value from the helper to Scratch. In Scratch, a block that can receive this value is called a reporter block, and when it receives a number or string, it is prefixed with " r " in the s2e file. On the other hand, when receiving a boolean value, add " b " to distinguish it.

To return a value to the reporter block, use the response to GET / poll. beep volume is associated with the name" volume ", so if you return volume 10 to GET / poll, the reporter block will be populated.

In the following, the value specified by set beep volume to (...) will be returned as it is. Here is the entire code so far.

testhelper.py


from aiohttp import web

volume = 0

async def handle_poll(request):
    text = "volume " + str(volume) + "\n"
    return web.Response(text=text)

async def handle_beep(request):
    print("play beep!")
    print("\007")
    return web.Response(text="OK")

async def handle_setvolume(request):
    global volume  #Don't forget
    tmp_volume = int(request.match_info['vol']) #Once to another variable
    if tmp_volume >= 0 and tmp_volume <= 10:
        volume = tmp_volume
        print("set volume= " + str(volume))
    else:
        print("out of range: " + str(tmp_volume))
    return web.Response(text="OK")

app = web.Application()
app.router.add_get('/poll', handle_poll)
app.router.add_get('/playBeep', handle_beep)
app.router.add_get('/setVolume/{vol}', handle_setvolume)

web.run_app(app, host='127.0.0.1', port=12345)

It seems better to make it a class and access it like self.volume. Is it like this in class?

Try it with Scratch. First, check the value next to the reporter block beep volume to display the value. Next, when you enter a number in the command block set beep volume to (...) and execute it (click the block), the value of the reporter block beep volume changes to the value specified in the command block. You can see that.

command_and_reporter.png

If there are multiple reporter blocks, return the response separated by the line feed code 0A.

Others: Waiting command block, etc.

Some command blocks wait to be executed, and are specified by prefixing " w " in s2e. To implement this block, you need to tell Scratch that the command is running.

For example, if you add " w " to set beep volume to (...), what was GET / setVolume / 5 becomes the command ID like GET / setVolume / 2574/5. Is added (2574 is an example, the value actually varies from command request to request). Adding this command ID to the response to poll tells Scratch which block is running. At this time, add it in the format _busy 2574. If you need to return multiple command IDs, separate them with a space.

In addition, there are various topics such as how to make a block select from a pull-down without entering a value, how to receive a character string, how to reset, cross-domain policy, etc. [Explanation PDF](https://wiki. It is mentioned in scratch.mit.edu/w/images/ExtensionsDoc.HTTP-9-11.pdf).

in conclusion

The above method may be a connection to Scratch 3 (I'm not sure what happens to the offline version of Scratch 3 and how to extend it), but with Python there are various things such as OpenCV, machine learning, numerical calculation, etc. Since the library is ready to use, it seems to be quite free to expand.

Of course, Node.js can also set up a lightweight server with similar asynchronous I / O, so if you are not particular about Python modules, you can write it in JavaScript. (I tried to summarize in this article)

If you want to keep the HTTP extension in Scratch 3 (and if it's currently being removed), you might change Scratch 3 as well.

Recommended Posts

Build a lightweight server in Python and listen for Scratch 2 HTTP extensions
Simple HTTP Server for python
Build a Python environment and transfer data to the server
<Python> Build a dedicated server for Jupyter Notebook data analysis
Library for specifying a name server and dig with python
Build a python environment on CentOS 7.7 for your home server
Write an HTTP / 2 server in Python
Build and test a CI environment for multiple versions of Python
Create a CGH for branching a laser in Python (laser and SLM required)
Get a token for conoha in python
Build a CentOS Linux 8 environment with Docker and start Apache HTTP Server
Quickly build a python environment for deep learning and data science (Windows)
Put Docker in Windows Home and run a simple web server with Python
Set up a simple HTTPS server in Python 3
Organize python modules and packages in a mess
I wrote a class in Python3 and Java
Set up a test SMTP server in Python.
Launch a web server with Python and Flask
Set up a simple SMTP server in Python
Automate background removal for the latest portraits in a directory with Python and API
Build API server for checking the operation of front implementation with python3 and Flask
Causes and solutions when python sam build fails in a newly created Cloud9 environment
I can't sleep until I build a server !! (Introduction to Python server made in one day)
Rock-paper-scissors poi in Python for beginners (answers and explanations)
Build an interactive environment for machine learning in Python
Building a Docker working environment for R and Python
Build a Python extension for E-Cell 4 on Windows 7 (64bit)
A simple Python HTTP server that supports Range Requests
Build a python environment for each directory with pyenv-virtualenv
Preferences for playing Wave in Python PyAudio and PortAudio
Try searching for a million character profile in Python
Problems and countermeasures for Otsu's binarization overflow in Python
Build Apache HTTP Server and Wildfly on Oracle Linux 8
Easy HTTP server and Systemd autostart settings in Go
Build a python virtual environment with virtualenv and virtualenvwrapper
Create a web server in Go language (net / http) (1)
Recursively search for files and directories in Python and output
Create a fake Minecraft server in Python with Quarry
Set a proxy for Python pip (described in pip.ini)
Http request in python
Seeking a unified way to wait and get for state changes in Selenium for Python elements
Parse the Researchmap API in Python and automatically create a Word file for the achievement list
[Sakura Rental Server] (For beginners) How to build an environment for Python, pyenv, and Flask. | For csh
Temporarily save a Python object and reuse it in another Python
List method argument information for classes and modules in Python
Create a child account for connect with Stripe in Python
[Python] Create a date and time list for a specified period
How to specify a public directory Python simple HTTP server
Build a Python development environment in Eclipse (add HTML editor)
Installation procedure for Python and Ansible with a specific version
A simple way to avoid multiple for loops in Python
Tips for coding short and easy to read in Python
A standard way to develop and distribute packages in Python
Explosive speed! Using Python Simple HTTP Server for kintone development
Useful tricks related to list and for statements in Python
Create code that outputs "A and pretending B" in python
Build Docker environment (Linux 8) and start Apache HTTP Server container
I created a class in Python and tried duck typing
Build a Python extension for E-Cell 4 on Mac OSX (Yosemite)
Problems and solutions when asked for MySQL db in Python 3
Just try to receive a webhook in ngrok and python