Metadata-Version: 2.4
Name: sirena-carbot
Version: 1.0.1
Summary: Python library for controlling Sirena carbot features over USB via LUCI_local protocol
License-Expression: MIT
Keywords: carbot,sirena,LUCI,serial,USB,automotive,window,mirror,child-lock,embedded
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: Topic :: System :: Hardware :: Hardware Drivers
Classifier: Topic :: Software Development :: Embedded Systems
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Operating System :: Microsoft :: Windows
Classifier: Operating System :: POSIX :: Linux
Classifier: Operating System :: MacOS
Requires-Python: >=3.8
Description-Content-Type: text/markdown
Requires-Dist: pyserial>=3.5
Provides-Extra: dev
Requires-Dist: build; extra == "dev"
Requires-Dist: twine; extra == "dev"

# sirena-carbot

A Python library for controlling Sirena carbot features over USB using the **LUCI_local** protocol.

---

## Table of Contents

- [Overview](#overview)
- [Requirements](#requirements)
- [Installation](#installation)
- [Quick Start](#quick-start)
- [Connecting to the Device](#connecting-to-the-device)
- [RX Monitor](#rx-monitor)
- [Available Controls](#available-controls)
  - [Window Controls](#window-controls)
  - [Mirror Controls](#mirror-controls)
  - [Child Lock Controls](#child-lock-controls)
- [How Commands Work](#how-commands-work)
- [Testing the Library](#testing-the-library)
- [Adding New Controls](#adding-new-controls)
- [Publishing Updates to PyPI](#publishing-updates-to-pypi)
- [Project Structure](#project-structure)
- [Changelog](#changelog)
- [Troubleshooting](#troubleshooting)

---

## Overview

`sirena-carbot` is a pip-installable Python library that lets you control car features (windows, mirrors, child lock, etc.) by sending **LUCI_local** commands to a carbot USB device over a serial connection.

Each control function follows a simple on/off pattern:

```python
front_right_window_up(1)   # ON  → sends command over USB
front_right_window_up(0)   # OFF → placeholder, does nothing
```

---

## Requirements

- Python 3.8 or higher
- A Sirena carbot USB device connected to your machine
- `pyserial` (installed automatically as a dependency)

---

## Installation

```bash
pip install sirena-carbot
```

### Installing from source (for development)

```bash
git clone https://github.com/your-org/sirena-carbot.git
cd sirena-carbot
pip install -e .
```

---

## Quick Start

```python
from sirena_carbot import *

enable_rx_monitor()        # opens a separate terminal showing RX data
connect()                  # auto-detects USB port

front_right_window_up(1)
mirror_fold(1)
child_lock_on(1)

disconnect()
```

### Wildcard import

```python
from sirena_carbot import *
```

Imports all public names:
`connect`, `disconnect`, `list_ports`, `enable_rx_monitor`,
all window / mirror / child-lock functions.

---

## Connecting to the Device

### Auto-detect port (recommended)

```python
connect()
```

### Specify port manually

```python
connect("COM3")                      # Windows
connect("/dev/ttyUSB0")              # Linux
connect("/dev/tty.usbserial-0001")   # macOS
```

### List available ports

```python
ports = list_ports()
for p in ports:
    print(p['port'], '-', p['description'])
```

### Connection parameters

| Parameter  | Value  |
|------------|--------|
| Baud rate  | 57600  |
| Data bits  | 8      |
| Parity     | None   |
| Stop bits  | 1      |

---

## RX Monitor

Call `enable_rx_monitor()` **before** `connect()` to automatically open a separate terminal window that displays all incoming serial data from the device in real time.

```python
from sirena_carbot import *

enable_rx_monitor()   # ← opens a new cmd window showing live RX data
connect()

front_right_window_up(1)
```

The monitor window looks like:

```
=== Sirena Carbot — RX Monitor ===

[12:34:01] RX: OK
[12:34:01] RX: LUCI_ACK 245
[12:34:02] RX: window_up done
```

**How it works internally:**
- Opens a local socket server on a free port
- Launches `sirena_carbot/_rx_monitor.py` in a new `cmd` window connected to that socket
- Attaches a log callback to `SerialManager` that forwards every RX line to the monitor window

---

## Available Controls

All control functions accept `state=1` (send command) or `state=0` (no-op placeholder).

**Return value:** `True` if the command was sent successfully, `False` otherwise.

```python
success = front_right_window_up(1)
if not success:
    print("Command failed – is the device connected?")
```

---

### Window Controls

| # | Function                     | Command sent (state=1)                   |
|---|------------------------------|------------------------------------------|
| 1 | `front_right_window_up(1)`   | `LUCI_local 245 front_right_window_up`   |
| 2 | `front_right_window_down(1)` | `LUCI_local 245 front_right_window_down` |
| 3 | `front_left_window_up(1)`    | `LUCI_local 245 front_left_window_up`    |
| 4 | `front_left_window_down(1)`  | `LUCI_local 245 front_left_window_down`  |
| 5 | `rear_right_window_up(1)`    | `LUCI_local 245 rear_right_window_up`    |
| 6 | `rear_right_window_down(1)`  | `LUCI_local 245 rear_right_window_down`  |
| 7 | `rear_left_window_up(1)`     | `LUCI_local 245 rear_left_window_up`     |
| 8 | `rear_left_window_down(1)`   | `LUCI_local 245 rear_left_window_down`   |

---

### Mirror Controls

| #  | Function           | Command sent (state=1)           |
|----|--------------------|----------------------------------|
|  9 | `mirror_fold(1)`   | `LUCI_local 245 mirror_fold`     |
| 10 | `mirror_unfold(1)` | `LUCI_local 245 mirror_unfold`   |

---

### Child Lock Controls

| #  | Function            | Command sent (state=1)          |
|----|---------------------|---------------------------------|
| 11 | `child_lock_on(1)`  | `LUCI_local 245 child_lock_on`  |
| 12 | `child_lock_off(1)` | `LUCI_local 245 child_lock_off` |

---

## How Commands Work

When you call a control function with `state=1`, the library sends a plain-text command over USB:

```
LUCI_local 245 <command_name>\n
```

When called with `state=0`, **nothing is sent** — it is a placeholder for future OFF implementation.

| Parameter   | Value      |
|-------------|------------|
| Protocol    | LUCI_local |
| Target      | 245        |
| Encoding    | UTF-8      |
| Line ending | `\n`       |

---

## Testing the Library

A test script is included at the root of the repository: `test_library.py`

```python
import time
from sirena_carbot import *

enable_rx_monitor()
connect()

front_right_window_up(1);    time.sleep(5)
front_right_window_down(1);  time.sleep(5)
front_left_window_up(1);     time.sleep(5)
front_left_window_down(1);   time.sleep(5)
rear_right_window_up(1);     time.sleep(5)
rear_right_window_down(1);   time.sleep(5)
rear_left_window_up(1);      time.sleep(5)
rear_left_window_down(1);    time.sleep(5)
mirror_fold(1);              time.sleep(5)
mirror_unfold(1);            time.sleep(5)
child_lock_on(1);            time.sleep(5)
child_lock_off(1);           time.sleep(5)

disconnect()
```

Run it:

```bash
python test_library.py
```

- Sends all 12 commands with a **5-second gap** between each for safety
- RX responses appear live in the separate monitor window
- Main terminal stays silent

---

## Adding New Controls

Add one line in `sirena_carbot/controls.py`:

```python
horn          = _make_control("horn")
headlights_on = _make_control("headlights_on")
door_lock     = _make_control("door_lock")
door_unlock   = _make_control("door_unlock")
```

Then export in `sirena_carbot/__init__.py`:

```python
from .controls import (
    # existing...
    horn,
    headlights_on,
    door_lock,
    door_unlock,
)

__all__ = [
    # existing...
    "horn",
    "headlights_on",
    "door_lock",
    "door_unlock",
]
```

---

## Publishing Updates to PyPI

### First time setup

1. Create an account at https://pypi.org
2. Go to **Account Settings → API tokens → Add API token**
3. Copy the token (starts with `pypi-`)
4. Save in `~/.pypirc`:

```ini
[distutils]
index-servers = pypi

[pypi]
username = __token__
password = pypi-YOUR_TOKEN_HERE
```

### Release workflow

```powershell
# 1. Bump version in pyproject.toml AND sirena_carbot/__init__.py

# 2. Clean old build artifacts
Remove-Item -Recurse -Force dist, build, *.egg-info

# 3. Build
python -m build

# 4. Upload
python -m twine upload dist/*
```

### Version numbering

| Change type              | Example             |
|--------------------------|---------------------|
| Bug fix                  | `1.0.0` → `1.0.1`  |
| New functions added      | `1.0.0` → `1.1.0`  |
| Breaking / major changes | `1.0.0` → `2.0.0`  |

> PyPI does **not** allow re-uploading the same version. Always bump before uploading.

---

## Project Structure

```
carbot_software/
├── pyproject.toml                   # Package metadata and build config
├── README.md                        # This file
├── test_library.py                  # Test script (sends all commands)
├── serial_communication.py          # Low-level USB serial driver
└── sirena_carbot/
    ├── __init__.py                  # Public API
    ├── connection.py                # Connection manager + enable_rx_monitor()
    ├── controls.py                  # Control functions (_make_control factory)
    └── _rx_monitor.py               # RX monitor window (launched automatically)
```

### Key classes (internal)

| Class             | File                       | Responsibility                                  |
|-------------------|----------------------------|-------------------------------------------------|
| `SerialManager`   | `serial_communication.py`  | Opens port, reads/writes bytes, manages threads |
| `ServoController` | `serial_communication.py`  | Builds `SetServoPartial` commands               |

---

## Changelog

### v1.0.0
- Added `enable_rx_monitor()` — opens a live RX data window in a separate terminal
- Added mirror controls: `mirror_fold`, `mirror_unfold`
- Added child lock controls: `child_lock_on`, `child_lock_off`
- `from sirena_carbot import *` supported via `__all__`
- Test script simplified to 15 lines with 5-second safety delay between commands

### v0.1.1
- Fixed `pyproject.toml` build backend (`setuptools.build_meta`)

### v0.1.0
- Initial release: window controls, `connect`, `disconnect`, `list_ports`

---

## Troubleshooting

### `RuntimeError: No serial ports found`

- Make sure the carbot USB device is plugged in
- Specify port manually: `connect("COM3")`
- Linux/macOS: `sudo usermod -aG dialout $USER` then log out and back in

### `Error: Not connected to serial port`

Always call `connect()` before any control function:

```python
from sirena_carbot import *
connect()
front_right_window_up(1)
```

### Command returns `False`

- Check the USB cable
- Ensure `connect()` returned `True`
- Reconnect:
  ```python
  disconnect()
  connect()
  ```

### RX Monitor window does not open

- Ensure you called `enable_rx_monitor()` **before** `connect()`
- Windows only: requires `cmd.exe` — not supported in Linux/macOS as-is

### Wrong COM port selected automatically

```python
for p in list_ports():
    print(p)

connect("COM5")
```

### `state=0` does nothing — is that a bug?

No. `state=0` is intentionally a **placeholder** until the firmware implements OFF behaviour.
