File size: 6,765 Bytes
02eac4b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
# LeRobot Arena Python Client

Python client library for the LeRobot Arena robotics API with separate Producer and Consumer classes.

## Installation

```bash
pip install -e .
```

Or with development dependencies:

```bash
pip install -e ".[dev]"
```

## Basic Usage

### Producer (Controller) Example

```python
import asyncio
from lerobot_arena_client import RoboticsProducer

async def main():
    # Create producer client
    producer = RoboticsProducer('http://localhost:8000')
    
    # List available rooms
    rooms = await producer.list_rooms()
    print('Available rooms:', rooms)
    
    # Create new room and connect
    room_id = await producer.create_room()
    await producer.connect(room_id)
    
    # Send initial state
    await producer.send_state_sync({
        'shoulder': 45.0,
        'elbow': -20.0
    })
    
    # Send joint updates (only changed values will be forwarded!)
    await producer.send_joint_update([
        {'name': 'shoulder', 'value': 45.0},
        {'name': 'elbow', 'value': -20.0}
    ])
    
    # Handle errors
    producer.on_error(lambda err: print(f'Error: {err}'))
    
    # Disconnect
    await producer.disconnect()

if __name__ == "__main__":
    asyncio.run(main())
```

### Consumer (Robot Executor) Example

```python
import asyncio
from lerobot_arena_client import RoboticsConsumer

async def main():
    consumer = RoboticsConsumer('http://localhost:8000')
    
    # Connect to existing room
    room_id = "your-room-id"
    await consumer.connect(room_id)
    
    # Get initial state
    initial_state = await consumer.get_state_sync()
    print('Initial state:', initial_state)
    
    # Set up event handlers
    def on_state_sync(state):
        print('State sync:', state)
    
    def on_joint_update(joints):
        print('Execute joints:', joints)
        # Execute on actual robot hardware
        for joint in joints:
            print(f"Moving {joint['name']} to {joint['value']}")
    
    def on_error(error):
        print(f'Error: {error}')
    
    # Register callbacks
    consumer.on_state_sync(on_state_sync)
    consumer.on_joint_update(on_joint_update)
    consumer.on_error(on_error)
    
    # Keep running
    try:
        await asyncio.sleep(60)  # Run for 60 seconds
    finally:
        await consumer.disconnect()

if __name__ == "__main__":
    asyncio.run(main())
```

### Factory Function Usage

```python
import asyncio
from lerobot_arena_client import create_client

async def main():
    # Create clients using factory function
    producer = create_client("producer", "http://localhost:8000")
    consumer = create_client("consumer", "http://localhost:8000")
    
    # Or use convenience functions
    from lerobot_arena_client import create_producer_client, create_consumer_client
    
    # Quick producer setup (auto-creates room and connects)
    producer = await create_producer_client('http://localhost:8000')
    print(f"Producer connected to room: {producer.room_id}")
    
    # Quick consumer setup (connects to existing room)
    consumer = await create_consumer_client(producer.room_id, 'http://localhost:8000')
    
    # Use context managers for automatic cleanup
    async with RoboticsProducer('http://localhost:8000') as producer:
        room_id = await producer.create_room()
        await producer.connect(room_id)
        await producer.send_state_sync({'joint1': 10.0})

if __name__ == "__main__":
    asyncio.run(main())
```

### Advanced Example: Producer-Consumer Pair

```python
import asyncio
from lerobot_arena_client import RoboticsProducer, RoboticsConsumer

async def run_producer(room_id: str):
    async with RoboticsProducer() as producer:
        await producer.connect(room_id)
        
        # Simulate sending commands
        for i in range(10):
            await producer.send_state_sync({
                'joint1': i * 10.0,
                'joint2': i * -5.0
            })
            await asyncio.sleep(1)

async def run_consumer(room_id: str):
    async with RoboticsConsumer() as consumer:
        await consumer.connect(room_id)
        
        def handle_joint_update(joints):
            print(f"🤖 Executing: {joints}")
            # Your robot control code here
        
        consumer.on_joint_update(handle_joint_update)
        
        # Keep listening
        await asyncio.sleep(15)

async def main():
    # Create room
    producer = RoboticsProducer()
    room_id = await producer.create_room()
    
    # Run producer and consumer concurrently
    await asyncio.gather(
        run_producer(room_id),
        run_consumer(room_id)
    )

if __name__ == "__main__":
    asyncio.run(main())
```

## API Reference

### RoboticsProducer

**Connection Methods:**
- `connect(room_id, participant_id=None)` - Connect as producer to room

**Control Methods:**
- `send_joint_update(joints)` - Send joint updates
- `send_state_sync(state)` - Send state synchronization (dict format)
- `send_emergency_stop(reason)` - Send emergency stop

**Event Callbacks:**
- `on_error(callback)` - Set error callback
- `on_connected(callback)` - Set connection callback  
- `on_disconnected(callback)` - Set disconnection callback

### RoboticsConsumer

**Connection Methods:**
- `connect(room_id, participant_id=None)` - Connect as consumer to room

**State Methods:**
- `get_state_sync()` - Get current state synchronously

**Event Callbacks:**
- `on_state_sync(callback)` - Set state sync callback
- `on_joint_update(callback)` - Set joint update callback
- `on_error(callback)` - Set error callback
- `on_connected(callback)` - Set connection callback
- `on_disconnected(callback)` - Set disconnection callback

### RoboticsClientCore (Base Class)

**REST API Methods:**
- `list_rooms()` - List all available rooms
- `create_room(room_id=None)` - Create a new room
- `delete_room(room_id)` - Delete a room
- `get_room_state(room_id)` - Get current room state
- `get_room_info(room_id)` - Get basic room information

**Utility Methods:**
- `send_heartbeat()` - Send heartbeat to server
- `is_connected()` - Check connection status
- `get_connection_info()` - Get connection details
- `disconnect()` - Disconnect from room

### Factory Functions

- `create_client(role, base_url)` - Create client by role ("producer" or "consumer")
- `create_producer_client(base_url, room_id=None)` - Create connected producer
- `create_consumer_client(room_id, base_url)` - Create connected consumer

## Requirements

- Python 3.12+
- aiohttp>=3.9.0
- websockets>=12.0

## Migration from v1

The old `RoboticsClient` is still available for backward compatibility but is now an alias to `RoboticsClientCore`. For new code, use the specific `RoboticsProducer` or `RoboticsConsumer` classes for better type safety and cleaner APIs.