This is the multi-page printable view of this section. Click here to print...

Return to the regular view of this page

As of 2025-07-24

TWELITE Wings API / MWings for Python

Latest Edition
    By using the TWELITE Wings API (MWings), you can control TWELITE from Python scripts on your PC.

    Library Overview

    The TWELITE Wings API (hereafter MWings) is a library for handling TWELITE from Python scripts.

    Features

    You can communicate with TWELITE child nodes through a TWELITE parent node connected to the host.

    • Interpret received data and convert it to a dictionary, JSON, or pandas DataFrame*
    • Send commands generated from dictionaries to the parent node
    • The Lite version does not support pandas.

    Example Use Cases

    For example, you can implement systems like the following:

    • Send temperature and humidity data received by MONOSTICK to a cloud server as JSON
    • Record acceleration data received by MONOSTICK to a CSV or Excel file*
    • Control an LED connected to TWELITE DIP via MONOSTICK from a PC
    • The Lite version cannot output directly to CSV or Excel files.

    Characteristics

    This is a module for modern Python, by modern Python, for modern Python.

    • With exceptions. Details explained later.

    Installation

    Available from PyPI.

    pip の場合

    
    
    pip install mwings

    poetry の場合

    
    
    poetry add mwings

    The Simplest Sample Script

    In just 6 lines, you can output received data from the Extremely Simple! Standard App (App_Twelite) in JSON format.

    import mwings as mw
    twelite = mw.Twelite(mw.utils.ask_user_for_port())
    @twelite.on(mw.common.PacketType.APP_TWELITE)
    def on_app_twelite(packet):
        print(packet.to_json())
    twelite.start()

    Environment Setup and Operation Check

    What You Need

    • PC
    • MONOSTICK (Parent and Repeater App / default settings)
    • TWELITE DIP (Extremely Simple! Standard App / default settings)
      • Connect peripherals such as switches (example: connect a tact switch between DI1 port and GND)

    Environment Setup

    Installing pyenv

    To manage the Python interpreter version, install pyenv.

    Linux
    
    
    curl https://pyenv.run | bash
    macOS
    
    
    brew update
    brew install pyenv
    Windows

    There is no pyenv for Windows. Instead, use pyenv-win.

    Invoke-WebRequest -UseBasicParsing -Uri "https://raw.githubusercontent.com/pyenv-win/pyenv-win/master/pyenv-win/install-pyenv-win.ps1" -OutFile "./install-pyenv-win.ps1"; &"./install-pyenv-win.ps1"

    Installing Python with pyenv / pyenv-win

    Install Python 3.12 or later (3.11 or later for Lite version), as required by MWings.

    To list available versions, run:

    pyenv install -l

    For example, to install Python 3.12.4 and apply it system-wide:

    pyenv install 3.12.4
    pyenv global 3.12.4

    To list installed versions, run:

    pyenv versions

    Installing pipx

    To manage command-line tools such as poetry in an isolated environment, install pipx.

    Linux

    Debian-based

    
    
    sudo apt update
    sudo apt install pipx
    pipx ensurepath

    From pip (recommended for Raspberry Pi)

    
    
    python3 -m pip install --user pipx
    python3 -m pipx ensurepath

    Fedora-based

    
    
    sudo dnf install pipx
    pipx ensurepath

    Reference: Installation - pipx

    macOS
    
    
    brew install pipx
    pipx ensurepath
    Windows
    scoop install pipx
    pipx ensurepath

    Installing poetry

    To manage the Python interpreter version and module dependencies for your project (similar to Node.js), install poetry.

    pipx install poetry

    Creating a Project

    Here, we will use mwtest as the project name.

    Move to the directory where you want to create the project and run:

    poetry new mwtest

    This will generate the mwtest directory.

    Project Setup

    Move into the project directory and link it to the Python version you installed earlier with pyenv.

    poetry env use 3.12.4

    Install MWings.

    poetry add mwings

    Creating the Simplest Sample Script

    First, let’s try running the script introduced earlier.

    import mwings as mw
    twelite = mw.Twelite(mw.utils.ask_user_for_port())
    @twelite.on(mw.common.PacketType.APP_TWELITE)
    def on_app_twelite(packet):
        print(packet.to_json())
    twelite.start()

    上記の内容で poetry が生成した __init__.py と同じ階層に simple.py を作成します。

    📁 mwtest
    └ 📁 mwtest
      ├ 📄 __init__.py
      └ 📄 simple.py

    Running the Simplest Sample Script

    Connect MONOSTICK and run:

    poetry run python simple.py

    If there are multiple serial ports, please select the serial port.

    If you get output in JSON string format, you have succeeded. An example of actual output is shown below.

    {
      "time_parsed": "2024-02-20T03:16:50.150386+00:00",
      "packet_type": "APP_TWELITE",
      "sequence_number": 13699,
      "source_serial_id": "810E0E23",
      "source_logical_id": 120,
      "lqi": 84,
      "supply_voltage": 3249,
      "destination_logical_id": 0,
      "relay_count": 0,
      "periodic": true,
      "di_changed": [
        true,
        true,
        false,
        false
      ],
      "di_state": [
        false,
        false,
        false,
        false
      ],
      "ai_voltage": [
        8,
        272,
        1032,
        112
      ],
      "mwings_implementation": "python",
      "mwings_version": "1.0.0",
      "hostname": "silverstone.local",
      "system_type": "Darwin"
    }

    Creating a Practical Script

    simple.py is just an explanatory sample. It is not a practical script.

    This is because twelite.start() creates a thread to receive data, but does not provide a way to terminate it. It is also a very hard-to-read script.

    Next, let’s create a more practical script. After running it, we will explain its contents.

    This time, we will set the following three conditions:

    Below is an example applying these points.

    # -*- coding:utf-8 -*-
    # Written for Python 3.12
    # Formatted with Black
    
    # MWings example: Receive data, print JSON, typed
    
    from zoneinfo import ZoneInfo
    
    import mwings as mw
    
    
    # Main function
    def main() -> None:
        # Create a twelite object
        twelite = mw.Twelite(mw.utils.ask_user_for_port())
    
        # Use JST for received data
        twelite.set_timezone(ZoneInfo("Asia/Tokyo"))
    
        # Register an event handler
        @twelite.on(mw.common.PacketType.APP_TWELITE)
        def on_app_twelite(packet: mw.parsers.app_twelite.ParsedPacket) -> None:
            print(packet.to_json(verbose=True, spread=False))
    
        # Start receiving
        try:
            # Set as daemon thread
            twelite.daemon = True
            # Start the thread, Join to the main thread
            twelite.start()
            print("Started receiving")
            while True:
                twelite.join(0.5)
        except KeyboardInterrupt:
            # Stop the thread
            print("Flushing...")
            twelite.stop()
            print("Completed")
    
    
    if __name__ == "__main__":
        # Call the main function
        main()

    Please save the above as practical.py.

    📁 mwtest
    └ 📁 mwtest
      ├ 📄 __init__.py
      ├ 📄 simple.py
      └ 📄 practical.py

    Running the Practical Script

    Run the following command to get output in JSON format, just like with simple.py.

    poetry run python practical.py

    However, this time you can exit with Ctrl+C without causing an error, and time_parsed should be in Japan Standard Time.

    Explanation of the Practical Script

    Code Explanation

    Let’s explain practical.py.

    import Statements

    In practical.py, two modules are imported:

    from zoneinfo import ZoneInfo
    
    import mwings as mw
    • zoneinfo.ZoneInfo is used to specify the time zone for the received timestamp.
    • mwings is the MWings library. It is imported as mw for brevity. For the Lite version, use mwingslite.
    Creating the Object

    The mw.Twelite object serves as the interface to access the TWELITE parent node connected to the host.

        # Create a twelite object
        twelite = mw.Twelite(mw.utils.ask_user_for_port())

    The mw.utils.ask_user_for_port() function gets a list of available serial ports on the host and returns the file descriptor path or COM port name selected by the user.

    Setting the Time Zone

    By default, the reception time of data is treated as UTC.

    In practical.py, this is set to JST.

        # Use JST for received data
        twelite.set_timezone(ZoneInfo("Asia/Tokyo"))

    Pass an IANA time zone identifier to ZoneInfo.

    Registering a Receive Handler

    To process data sent from TWELITE child nodes, register a receive handler. Here, in the receive handler for the Extremely Simple! Standard App, the received data is converted to JSON format and output.

        # Register an event handler
        @twelite.on(mw.common.PacketType.APP_TWELITE)
        def on_app_twelite(packet: mw.parsers.app_twelite.ParsedPacket) -> None:
            print(packet.to_json(verbose=True, spread=False))

    You can register a receive handler by applying the twelite.on() decorator ? to any function.

    The data class ParsedPacket received by the handler has methods such as to_json() to convert to a JSON string, to_dict() to convert to a dictionary, and to_df() to convert to a pandas DataFrame.

    Here, the to_json() method is used to convert to a JSON string.

            print(packet.to_json(verbose=True, spread=False))
    Starting and Stopping Reception

    mw.Twelite inherits from threading.Thread. In practical.py, twelite.start() starts the receive process in a separate thread, and twelite.stop() stops it.

        # Start receiving
        try:
            # Set as daemon thread
            twelite.daemon = True
            # Start the thread, Join to the main thread
            twelite.start()
            print("Started receiving")
            while True:
                twelite.join(0.5)
        except KeyboardInterrupt:
            # Stop the thread
            print("Flushing...")
            twelite.stop()
            print("Completed")

    Setting twelite.daemon to True makes the receiving subthread a daemon. When all non-daemon threads have exited, the whole Python program will also exit. Here, the main thread is kept waiting by repeatedly calling twelite.join().

    When the main thread detects Ctrl+C input, it calls twelite.stop() in the except block to stop the receive process. twelite.stop() waits until the subthread finishes calling the receive handler.

    Supplementary Explanation

    PEP8 Compliance

    practical.py and the MWings source code are formatted using the Black code formatter, which is compatible with PEP8.

    Except for the maximum line length, Black does not accept any configuration, making it a stubborn formatter, but it saves the trouble of formulating and sharing coding standards. Coding rules are often contentious, but spending resources on such trivial matters rather than on productive work goes against the Python spirit.

    To add Black to your project, run:

    poetry add --group dev black

    You can run it on specific files or directories:

    poetry run black mwtest

    You can also check without formatting:

    poetry run black --check mwtest
    Type Hint Support

    practical.py and the MWings source code support type hints.

    The MWings library uses mypy, the official static type checker for Python.

    To add mypy to your project, run:

    poetry add --group dev mypy

    Like Black, you can specify files or directories to check:

    poetry run mypy mwtest

    Practical Script Applications

    We provide scripts that further develop practical.py.

    mwings_python/examples at main

    log_export.py

    Reads a text file containing output from the parent node, interprets it, saves the results as a pandas DataFrame, and finally outputs to a CSV or Excel file. Not supported in the Lite version.

    rx_export.py

    Receives output from the connected parent node, interprets the results, saves them as a pandas DataFrame, and finally outputs to a CSV or Excel file. Not supported in the Lite version.

    • If you select a CSV file, all results are saved in a single file.
    • If you select an Excel file instead, the results are saved in separate sheets by packet type.

    rx_export_csv_durable.py

    Receives output from the connected parent node, interprets the results, and appends them to CSV files by source serial ID. Not supported in the Lite version.

    rx_print_df.py

    Receives output from the connected parent node, simply converts the interpreted results to a pandas DataFrame, and outputs them as a string. Not supported in the Lite version.

    rx_print_dict.py

    Receives output from the connected parent node, simply converts the interpreted results to a dictionary, and outputs them. Also supported in the Lite version.

    rx_print_json.py

    Receives output from the connected parent node, simply converts the interpreted results to a JSON string, and outputs them. Also supported in the Lite version.

    tx_binary_uart.py

    Sends binary data [0xBE, 0xEF] to TWELITE UART via the connected parent node running the Serial Communication App. Also supported in the Lite version.

    Blinks the LED connected to the DO1 port of TWELITE DIP via the connected Parent and Repeater App parent node. Also supported in the Lite version.

    Turns on the LED of the Notification PAL in each color via the connected Parent and Repeater App parent node. Also supported in the Lite version.

    API Reference