Metadata-Version: 2.4
Name: fluid-reality
Version: 0.2.5
Summary: Python SDK for Fluid Reality hardware.
Project-URL: Homepage, https://github.com/Fluid-Reality/sdk
Project-URL: Repository, https://github.com/Fluid-Reality/sdk
Project-URL: Issues, https://github.com/Fluid-Reality/sdk/issues
Author: Fluid Reality
License-Expression: MIT
License-File: LICENSE
Keywords: actuators,fluid-reality,haptics,hardware,serial
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Scientific/Engineering
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.10
Requires-Dist: pyserial>=3.5
Requires-Dist: pyyaml>=6.0
Provides-Extra: bluetooth
Requires-Dist: bleak>=0.22; extra == 'bluetooth'
Provides-Extra: test
Requires-Dist: bleak>=0.22; extra == 'test'
Requires-Dist: pytest>=8; extra == 'test'
Description-Content-Type: text/markdown

# Fluid Reality SDK

Python SDK for Fluid Reality hardware.

The package name on PyPI is `fluid-reality`; the Python import package is
`fluid_reality`.

Hardware setup guides:

- [Rockford Development Kit Start Here](docs/rockford_kit_start_here/README.md)
- [Lansing Development Kit Start Here](docs/lansing_kit_start_here/README.md)

## Install

Use Python 3.10 or newer.

```bash
python -m pip install --upgrade pip
python -m pip install fluid-reality
```

## Connecting to the Controller

A new controller is available over USB serial by default. Connect by serial
first, then use the
[Dashboard](apps/fluidreality_dashboard/README.md#board-tools) to configure
Bluetooth, Wi-Fi, TCP, access-token authentication, or TLS. The SDK can then
connect by serial, TCP/TLS, or Bluetooth.

You can pass an endpoint directly to `Rockford(...)` or `Lansing(...)`, or save
the connection settings in a YAML connection file. A connection file keeps the
transport, address, and authentication settings together so applications and
examples can open the same controller consistently:

```python
from fluid_reality import Rockford

with Rockford.from_connection_file("rockford.connection.yaml") as board:
    print(board.status())
```

Use the board as a context manager, as shown above, so the connection closes
cleanly.

Every connection file starts with these fields:

```yaml
format: fluid-reality-connection
version: 1
transport: serial  # serial, tcp, tls, or bluetooth
```

The remaining fields depend on the transport. Connection files can contain
access tokens, so store and share them as private configuration. They never
contain TLS private keys.

### Serial

Serial is the only connection method available before the controller is
configured. Connect the controller to the computer over USB, then list the
serial devices visible to Python:

```bash
python -m serial.tools.list_ports
```

Typical device names are `COM4` on Windows, `/dev/cu.usbmodem...` on macOS, and
`/dev/ttyACM...` or `/dev/ttyUSB...` on Linux. If several devices are listed,
unplug the controller, run the command again, reconnect it, and look for the new
entry.

Connect directly:

```python
from fluid_reality import Rockford

with Rockford("COM18") as board:
    print(board.status())
```

A serial connection file uses `serial_port` and an optional `baudrate`. The
default baud rate is `250000`:

```yaml
format: fluid-reality-connection
version: 1
transport: serial
serial_port: COM18
baudrate: 250000
```

### TCP/TLS

Configure Wi-Fi, the TCP server, access-token authentication, and TLS from the
[Dashboard](apps/fluidreality_dashboard/README.md#board-tools) while connected
over USB serial. TCP sends unencrypted traffic. TLS encrypts the connection
and should verify the controller certificate.

Connect directly over TCP:

```python
from fluid_reality import Rockford

with Rockford(
    "tcp://192.168.1.64:49765",
    network_token="your-device-token",
) as board:
    print(board.network_status())
```

A TCP connection file uses `host`, `port`, and an optional `access_token`:

```yaml
format: fluid-reality-connection
version: 1
transport: tcp
host: 192.168.1.64
port: 49765
access_token: your-device-token
```

Connect directly over TLS with the public certificate created or installed in
the Dashboard:

```python
from fluid_reality import Rockford

with Rockford(
    "tls://192.168.1.64:49765",
    network_token="your-device-token",
    tls_ca_file="rockford-certificate.pem",
    tls_server_hostname="rockford.local",
) as board:
    print(board.network_status())
```

A TLS connection file adds a `tls` section. `certificate_file` may be absolute
or relative to the connection file. `server_hostname` is the name in the
certificate:

```yaml
format: fluid-reality-connection
version: 1
transport: tls
host: 192.168.1.64
port: 49765
access_token: your-device-token
tls:
  certificate_file: rockford-certificate.pem
  server_hostname: rockford.local
  verify_hostname: true
```

The `tls` section can use `certificate` instead of `certificate_file` to embed
the PEM certificate in the YAML file. Certificate and hostname verification are
enabled by default. Set `verify_certificate: false` only when the controller is
on a trusted network and its certificate cannot be verified by the client.

For local development, the
[Fluid Reality Simulator](apps/fluidreality_simulator/README.md) listens at
`tcp://127.0.0.1:49765`. The [Device Bridge](apps/device_bridge/README.md) can
expose a serial controller over TCP or TLS and capture TX/RX traffic.

### Bluetooth

Enable and configure Bluetooth from the
[Dashboard](apps/fluidreality_dashboard/README.md#board-tools) while connected
over USB serial. Install the optional Bluetooth dependency:

```bash
python -m pip install "fluid-reality[bluetooth]"
```

Use [09_bluetooth_discovery.py](examples/09_bluetooth_discovery.py) to discover
nearby controllers and connect to one by its displayed index:

```bash
python examples\09_bluetooth_discovery.py
python examples\09_bluetooth_discovery.py --connect 0 --pair
```

Run the example with `--help` for discovery timeout and access-token options.

A Bluetooth connection file uses `device`, optional operating-system pairing,
and an optional access token:

```yaml
format: fluid-reality-connection
version: 1
transport: bluetooth
device: FR-Rockford-3D3731
pair: true
access_token: optional-token
```

## Touch Validation Example

The maintained
[basic actuator example](examples/01_basic_actuator_current.py) connects to a
board, enables power, waits for a valid supply-voltage reading, detects an
actuator, runs a bounded pulse, measures current, and shuts output and power
down even if an error occurs.

```powershell
python examples\01_basic_actuator_current.py COM18 --actuator 0
```

Run the example with `--help` to see all connection and pulse options.

## Core Concepts

After connecting to the controller, call `board.power_on()`. Detect each
actuator before using it:

```python
state = board.detect(0)
```

Detection records one of these states:

- `Unknown`: the actuator has not been detected during this connection.
- `Present`: an actuator was found, but detection has not finished evaluating
  it.
- `Ready`: detection passed and the actuator can be driven.
- `Not connected`: no actuator was found at that port.
- `Error`: the actuator was found but is outside the normal operating range.
  Run `board.initialize(actuator)` before using it. If it remains in `Error`,
  leave it off, check the connection, and contact Fluid Reality support.

Use `board.set_actuator(actuator, value)` to control actuators one at a time.
The actuator must be `Ready`. Values range from `0` to `255`; `0` stops the
actuator and starts its discharge phase:

```python
board.set_actuator(0, 255)
board.set_actuator(0, 0)
```

For frequent updates or coordinated motion, use streaming. Enter stream mode,
send values with `stream_actuator()` or `stream_values()`, and always exit stream
mode when finished. Stream only to actuators that have passed detection. The
`stream_sine()` helper generates a timed sine wave.

## Discharge Behavior

Discharge reverses accumulated drive and returns the actuator toward a neutral
state. This balancing helps maintain performance and extend actuator life.

Actuator output and discharge are separate phases. The controller firmware integrates
each actuator's signed voltage-time exposure and maintains a 1:1 forward/reverse
balance. Its default per-actuator budget is 10,000 V·s, equivalent to 200 V for
50 seconds.

When the controller receives a normal off command, it immediately applies full
reverse until the accumulated VT is cancelled. If an actuator exhausts its VT
budget while still active, firmware gently ramps from full forward to full
reverse at 100 V/s, includes the ramp in the VT calculation, then holds full
reverse until the balance reaches zero. There is no separate continuous
activation-time limit. During discharge, an actuator can still feel
active or busy even after it was commanded off; that is expected.

Wait for discharge to finish before starting the next pulse or interpreting the
actuator as idle. The SDK and firmware use this discharge phase to return the
actuator safely toward neutral.

Read the controller's VT settings with `board.read_config()` or
`board.vt_limit_vs()`. Setting `board.vt_limit_vs(value)` uses whole V·s and
permanently marks the board as user-modified. An incorrect limit can permanently
damage actuators or board electronics. Factory reset restores 10,000 V·s but
does not erase that audit marker. See `examples/13_vt_budget.py` for the guarded
configuration flow.

## Recovering an Actuator After Detection

Do not drive an actuator unless detection ends in `Ready`. If it ends in another
state, use the state to choose the next step:

- `Unknown` means detection has not completed during the current connection.
  Make sure the board is powered on, then use **Redetect all actuators** in the
  dashboard.
- `Present` means the actuator was found, but its evaluation did not finish.
  Run detection again. If it remains in this state, check the connection and
  the dashboard Event Log for an interrupted command or communication error.
- `Not connected` means the controller did not measure the expected response
  from that port. First confirm that the barrel connector is fully seated and
  supplying 5 V. Without the 5 V input, connected actuators can be reported as
  `Not connected`. Turn the board off before handling the actuator wiring, then
  confirm that an actuator is connected to the selected port and that its keyed
  plug is fully seated. Inspect the plugs, cables, and port for damage, power the
  board on, and run detection again. Missing 5 V power, a loose or damaged
  connection, the wrong selected port, or a disconnected actuator can cause
  this result.
- `Error` means the actuator was found, but its measured response is outside the
  normal operating range. This can result from an actuator that needs
  conditioning or from a problem with the actuator, cable, connector, or port.
  Leave normal output off. In the dashboard, select the actuator and run
  **Initialize**, followed by **Diagnose**. If Diagnose still ends in `Error`,
  run **Recover**, then run **Diagnose** again to update the state.

If an actuator still does not reach `Ready`, turn the board off and compare it
with a known-good actuator and port to isolate the cable, actuator, or controller
connection. Stop using any damaged component. If the problem remains, save the
dashboard results and Event Log and contact Fluid Reality support.

See the dashboard's
[Actuator Tools](apps/fluidreality_dashboard/README.md#actuator-tools) for
Initialize, Diagnose, and Recover instructions.

## API Reference

For the complete customer development API reference, including all public
classes, methods, errors, debug output options, streaming helpers, and code
examples, see [docs/api_reference.md](docs/api_reference.md).

## Dashboard

The Fluid Reality Dashboard configures and operates the
controllers over USB serial, Bluetooth LE, TCP, or TLS. It provides power,
telemetry, detection, initialization, diagnosis, recovery, and square-wave
controls.

See [apps/fluidreality_dashboard/README.md](apps/fluidreality_dashboard/README.md)
for installation and usage instructions.

## Terminal

The Fluid Reality Terminal connects to the controllers,
controls power and actuator output, displays telemetry and configuration, and
runs detection, diagnosis, initialization, recovery, and square-wave tests. It
supports interactive use, command sequences, and newline-delimited JSON output
for automation.

The terminal directory also includes PowerShell, Linux/macOS shell, and Windows
batch workflows for detecting actuators and initializing them to a target
current delta while monitoring improvement and enforcing bounded stop
conditions.

See [apps/fluidreality_terminal/README.md](apps/fluidreality_terminal/README.md) for
installation, the complete command reference, JSON schemas, automation options,
and platform-specific usage instructions.

## Examples

Example scripts are available in [examples](examples). Board examples accept a
USB serial port, a `tcp://`, `tls://`, or `ble://` endpoint, or a YAML profile:

```powershell
python examples\05_status_snapshot.py COM18
python examples\05_status_snapshot.py tcp://192.168.24.1:49765 --access-token TOKEN
python examples\05_status_snapshot.py --connection-file board.connection.yaml
python examples\05_status_snapshot.py COM5 --board lansing
```

Rockford is the default hardware profile. Pass `--board lansing` for Lansing.
Use `python <example> --help` for each example's complete options.

- [01_basic_actuator_current.py](examples/01_basic_actuator_current.py):
  power the board, connect the output, detect one actuator, pulse it, and read
  current.
- [02_initialize_and_diagnose.py](examples/02_initialize_and_diagnose.py):
  detect an actuator, initialize it when needed, and report diagnosis results.
- [03_stream_sine.py](examples/03_stream_sine.py):
  stream a sine waveform to one actuator with board-specific Lansing timing or
  guarded Rockford VT configuration.
- [04_debug_logging.py](examples/04_debug_logging.py):
  enable SDK and firmware debug output and save it to a log file.
- [05_status_snapshot.py](examples/05_status_snapshot.py):
  print firmware identity, capabilities, and a full status snapshot.
- [06_manual_output_bench_test.py](examples/06_manual_output_bench_test.py):
  run low-level output and timed-current bench commands using the board's
  electrical model.
- [07_error_handling.py](examples/07_error_handling.py):
  show how to catch SDK exceptions and print recovery guidance.
- [08_actuator_pulse_until_key.py](examples/08_actuator_pulse_until_key.py):
  repeatedly pulse one actuator until a key is pressed.
- [09_bluetooth_discovery.py](examples/09_bluetooth_discovery.py):
  discover Fluid Reality Bluetooth boards and optionally connect to one.
- [10_network_configuration.py](examples/10_network_configuration.py):
  inspect or update Wi-Fi mode, IP, hostname, and TCP settings.
- [11_firmware_update.py](examples/11_firmware_update.py):
  upload and verify a firmware image over USB, TCP, or TLS.
- [12_factory_reset.py](examples/12_factory_reset.py):
  factory-reset a Rockford board over USB with explicit confirmation.
- [13_vt_budget.py](examples/13_vt_budget.py):
  inspect Rockford's VT budget and require explicit risk confirmation before a
  persistent change.
