Metadata-Version: 2.1
Name: mwings
Version: 1.0.0
Summary: Communicate with TWELITE wireless modules
License: MW-OSSLA
Author: Mono Wireless Inc.
Author-email: mono-oss@mono-wireless.com
Requires-Python: >=3.12,<4.0
Classifier: License :: Other/Proprietary License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.12
Requires-Dist: overrides (>=7.4.0,<8.0.0)
Requires-Dist: pandas (>=2.2.0,<3.0.0)
Requires-Dist: pandas-stubs (>=2.1.4.231227,<3.0.0.0)
Requires-Dist: pyarrow (>=15.0.0,<16.0.0)
Requires-Dist: pydantic (>=2.5.2,<3.0.0)
Requires-Dist: pyee (>=11.1.0,<12.0.0)
Requires-Dist: pyserial (>=3.5,<4.0)
Requires-Dist: xlsxwriter (>=3.1.9,<4.0.0)
Description-Content-Type: text/markdown

<a href="https://mono-wireless.com/jp/index.html">
    <img src="https://mono-wireless.com/common/images/logo/logo-land.svg" alt="mono wireless logo" title="MONO WIRELESS" align="right" height="60" />
</a>

# MWings

A library that communicate with TWELITE wireless modules.

[![Lint with mypy / black / ruff](https://github.com/dev-alnasl/mwings_python_dev/actions/workflows/lint.yml/badge.svg)](https://github.com/dev-alnasl/mwings_python_dev/actions/workflows/lint.yml)
[![MW-OSSLA](https://img.shields.io/badge/License-MW--OSSLA-e4007f)](LICENSE)

## Overview

Receive packets from and send commands to TWELITE child devices through the connected TWELITE parent device.

The `App_Wings` firmware must be written to the TWELITE parent device connected to the host.

Built for Python 3.12+.

### Receive packets from

  - `App_Twelite`
  - `App_IO`
  - `App_ARIA` (ARIA mode)
  - `App_CUE` (CUE mode, PAL Event (Move/Dice) mode)
  - `App_PAL`
    - AMBIENT
    - OPENCLOSE
    - MOTION
  - `App_Uart` (Mode A, simple/extended)

### Send commands to

  - `App_Twelite` (Signal)
  - `App_PAL` NOTICE
    - Simple
    - Detailed
    - Event
  - `App_Uart` (Mode A, simple)

## Feature

### Written with modern python

*Modules of the modern python, by the modern python, for the modern python.*

- Built with `poetry` and `pyproject.toml`
- Fully typed; passes `mypy --strict`
- PEP8 compliance; formatted with `black`, passes `ruff check`
- docstring everywhere (numpy-style)
- Data classes are derived from `pydantic.BaseModel`

### Great data portability

Received data can be exported easily.

- [`to_dict()`](https://monowireless.github.io/mwings_python/mwings.html#mwings.common.ParsedPacketBase.to_dict) for dictionary
- [`to_json()`](https://monowireless.github.io/mwings_python/mwings.html#mwings.common.ParsedPacketBase.to_json) for JSON string
- [`to_df()`](https://monowireless.github.io/mwings_python/mwings.html#mwings.common.ParsedPacketBase.to_df) for Dataframe (requires pandas)
    - [`to_csv()`](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.to_csv.html) for CSV file (with pandas)
    - [`to_excel()`](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.to_excel.html) for Xlsx file (with pandas)

## Examples

### Receive App_Twelite packets

#### Using `Twelite.receive()`

Simplest way to receive some parsed packets.

Below script shows how to receive App_Twelite packets in blocking operations.

```python
import mwings as mw


def main() -> None:
    # Create twelite object
    twelite = mw.Twelite(mw.utils.ask_user_for_port())

    # Attach handlers
    @twelite.on(mw.Twelite.Packet.APP_TWELITE)
    def on_app_twelite(packet: mw.parsers.app_twelite.ParsedPacket) -> None:
        print(packet.to_json())

    # Receive packets
    while True:
        print(twelite.receive())


if __name__ == "__main__":
    try:
        main()
    except KeyboardInterrupt:
        print("...Aborting")
```

#### Using `Twelite.start()`

Practical way to receive some parsed packets.

The `Twelite` class is a subclass of the [`threading.Thread`](https://docs.python.org/3/library/threading.html#threading.Thread).

Below script shows how to receive packets in another thread.

```python
import mwings as mw


def main() -> None:
    # Create twelite object
    twelite = mw.Twelite(mw.utils.ask_user_for_port())

    # Attach handlers
    @twelite.on(mw.Twelite.Packet.APP_TWELITE)
    def on_app_twelite(packet: mw.parsers.app_twelite.ParsedPacket) -> None:
        print(packet.to_json())

    # Start receiving
    try:
        twelite.daemon = True
        twelite.start()
        twelite.join()
        print("Started receiving")
    except KeyboardInterrupt:
        print("...Stopping")
        twelite.stop()
        print("Stopped")


if __name__ == "__main__":
    main()
```

Note that event handlers are not called from the main thread.

When you have to use parsed data from the main thread, data should be passed by `queue` or something.

### Send App_Twelite packets

To send packets, just create a command and send it.

Below script shows how to blink an LED on the DO1 port.

```python
from time import sleep
from typing import Any

import mwings as mw


def main() -> None:
    # Create twelite objec
    twelite = mw.Twelite(mw.utils.ask_user_for_port())

    # Create command (initialize in pydantic style)
    initial: dict[str, Any] = {
        "destination_logical_id": 0x78,
        "di_to_change": [True, False, False, False],
        "di_state": [False, False, False, False],
    }
    command = mw.serializers.app_twelite.Command(**initial)

    # Blinking
    while True:
        command.di_state[0] = not command.di_state[0]
        twelite.send(command)
        print(f"Flip DO1: {command.di_state[0]}")
        sleep(1)


if __name__ == "__main__":
    try:
        main()
    except KeyboardInterrupt:
        print("...Aborting")
```

Note that command data classes (such as `mw.serializers.app_twelite.Command`) are derived from [`pydantic.BaseModel`](https://docs.pydantic.dev/latest/api/base_model/#pydantic.BaseModel).

## LICENSE

MW-OSSLA

