dexter-controller 0.2.2__tar.gz → 1.0.0a1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. dexter_controller-1.0.0a1/PKG-INFO +273 -0
  2. dexter_controller-1.0.0a1/README.md +248 -0
  3. dexter_controller-1.0.0a1/pyproject.toml +37 -0
  4. dexter_controller-1.0.0a1/pyproject.toml.orig +35 -0
  5. dexter_controller-1.0.0a1/src/dexter_controller/__init__.py +58 -0
  6. dexter_controller-1.0.0a1/src/dexter_controller/ble_dexter_device.py +507 -0
  7. dexter_controller-1.0.0a1/src/dexter_controller/calibration.py +261 -0
  8. dexter_controller-1.0.0a1/src/dexter_controller/data_recorder.py +209 -0
  9. dexter_controller-1.0.0a1/src/dexter_controller/dexter_device.py +445 -0
  10. dexter_controller-1.0.0a1/src/dexter_controller/finger.py +49 -0
  11. dexter_controller-1.0.0a1/src/dexter_controller/finger_orientation.py +38 -0
  12. dexter_controller-1.0.0a1/src/dexter_controller/force_converter.py +50 -0
  13. dexter_controller-1.0.0a1/src/dexter_controller/force_processor.py +43 -0
  14. dexter_controller-1.0.0a1/src/dexter_controller/harp/dexter/__init__.py +503 -0
  15. dexter_controller-1.0.0a1/src/dexter_controller/harp/dexter/py.typed +0 -0
  16. dexter_controller-1.0.0a1/src/dexter_controller/recording.py +42 -0
  17. dexter_controller-1.0.0a1/src/dexter_controller/recording_format.py +16 -0
  18. dexter_controller-1.0.0a1/src/dexter_controller/recording_metadata.py +137 -0
  19. dexter_controller-1.0.0a1/src/dexter_controller/recording_reader.py +270 -0
  20. dexter_controller-1.0.0a1/src/dexter_controller/tare.py +87 -0
  21. dexter_controller-1.0.0a1/src/dexter_controller/uart_dexter_device.py +468 -0
  22. dexter_controller-1.0.0a1/src/dexter_controller/writers.py +258 -0
  23. dexter_controller-0.2.2/PKG-INFO +0 -57
  24. dexter_controller-0.2.2/README.md +0 -44
  25. dexter_controller-0.2.2/pyproject.toml +0 -21
  26. dexter_controller-0.2.2/src/dexter_controller/__init__.py +0 -12
  27. dexter_controller-0.2.2/src/dexter_controller/ble_loadcell_device.py +0 -152
  28. dexter_controller-0.2.2/src/dexter_controller/dexter_hand_controller.py +0 -253
  29. dexter_controller-0.2.2/src/dexter_controller/finger.py +0 -16
  30. dexter_controller-0.2.2/src/dexter_controller/loadcell_device.py +0 -54
@@ -0,0 +1,273 @@
1
+ Metadata-Version: 2.3
2
+ Name: dexter-controller
3
+ Version: 1.0.0a1
4
+ Summary: Dexter device Controller
5
+ Author: Hardware and Software Platform, Champalimaud Foundation
6
+ Author-email: Hardware and Software Platform, Champalimaud Foundation <software@research.fchampalimaud.org>
7
+ Classifier: Programming Language :: Python :: 3
8
+ Classifier: Programming Language :: Python :: 3.11
9
+ Classifier: Programming Language :: Python :: 3.12
10
+ Classifier: Programming Language :: Python :: 3.13
11
+ Requires-Dist: bleak==2.1.1
12
+ Requires-Dist: harp-protocol==0.5.0
13
+ Requires-Dist: harp-serial==0.5.0
14
+ Requires-Dist: pydoc-markdown>=4.8.2 ; extra == 'docs'
15
+ Requires-Dist: pyarrow>=14.0.0 ; extra == 'parquet'
16
+ Requires-Dist: dexter-controller[reader] ; extra == 'parquet'
17
+ Requires-Dist: numpy>=1.25.0 ; extra == 'reader'
18
+ Requires-Python: >=3.11
19
+ Project-URL: Repository, https://github.com/fchampalimaud/dexter-controller/
20
+ Project-URL: Bug Tracker, https://github.com/fchampalimaud/dexter-controller/issues
21
+ Provides-Extra: docs
22
+ Provides-Extra: parquet
23
+ Provides-Extra: reader
24
+ Description-Content-Type: text/markdown
25
+
26
+ # dexter-controller
27
+
28
+ [![PyPI](https://img.shields.io/pypi/v/dexter-controller)](https://pypi.org/project/dexter-controller/)
29
+ [![Python](https://img.shields.io/pypi/pyversions/dexter-controller)](https://pypi.org/project/dexter-controller/)
30
+
31
+ Python library for the **Dexter** hand device. This library handles device communication, force computation, calibration, and data recording.
32
+
33
+ ## Installation
34
+
35
+ The recommended way to install the library is via `uv`. In your project add the dependency with:
36
+
37
+ ```bash
38
+ uv add dexter-controller
39
+ # or use pip
40
+ pip install dexter-controller
41
+ ```
42
+
43
+ Optional extras for reading and writing recordings:
44
+
45
+ ```bash
46
+ uv add dexter-controller[reader] # numpy, for loading recordings
47
+ uv add dexter-controller[parquet] # pyarrow + numpy, for Parquet format support
48
+ # or use pip
49
+ pip install dexter-controller[reader] # numpy, for loading recordings
50
+ pip install dexter-controller[parquet] # pyarrow + numpy, for Parquet format support
51
+ ```
52
+
53
+ ## Quick start
54
+
55
+ Connect over BLE or UART using the `DexterDevice` factory:
56
+
57
+ ```python
58
+ import time
59
+ from dexter_controller import DexterDevice, Finger
60
+
61
+ device = DexterDevice.create_ble(
62
+ # connect to a specific device by name:
63
+ # device_name="Dexter-001"
64
+ )
65
+ device.connect()
66
+ device.start_acquisition()
67
+
68
+ device_name = device.get_device_name()
69
+ battery = device.get_battery_level()
70
+ rate = device.get_sampling_rate()
71
+ print(f"Device: {device_name} | Battery: {battery}% | Rate: {rate.value} Hz")
72
+
73
+ try:
74
+ while True:
75
+ for finger in Finger:
76
+ data = device.finger_data[finger]
77
+ print(f"{finger.name}: {data.raw_data}", end=" ")
78
+ print(end="\r", flush=True)
79
+ time.sleep(0.02)
80
+ except KeyboardInterrupt:
81
+ pass
82
+ finally:
83
+ device.close()
84
+ ```
85
+
86
+ ## Force computation
87
+
88
+ Convert raw load-cell readings into calibrated 2D force vectors (Fx, Fy):
89
+
90
+ ```python
91
+ from dexter_controller import (
92
+ DEFAULT_CALIBRATION,
93
+ DexterDevice,
94
+ Finger,
95
+ ForceProcessor,
96
+ get_default_orientation,
97
+ )
98
+
99
+ device = DexterDevice.create_ble()
100
+ device.connect()
101
+ device.start_acquisition()
102
+
103
+ processors = {}
104
+ for finger in Finger:
105
+ orientation = get_default_orientation(finger)
106
+ processors[finger] = ForceProcessor(DEFAULT_CALIBRATION, orientation)
107
+
108
+ # In your read loop:
109
+ data = device.finger_data[Finger.INDEX]
110
+ fx, fy = processors[Finger.INDEX].process(data)
111
+ ```
112
+
113
+ Each `ForceProcessor` includes a `TareState` that lets you zero-out the baseline:
114
+
115
+ ```python
116
+ processors[Finger.INDEX].tare.start_tare(sample_count=20)
117
+ ```
118
+
119
+ ## Recording data
120
+
121
+ Record sessions to disk in CSV, SQLite (**default**), or Parquet format:
122
+
123
+ ```python
124
+ from dexter_controller import (
125
+ DEFAULT_CALIBRATION,
126
+ DataRecorder,
127
+ DexterDevice,
128
+ Finger,
129
+ ForceProcessor,
130
+ RecordingMetadata,
131
+ get_default_orientation,
132
+ )
133
+ from dexter_controller.recording_format import RecordingFormat
134
+
135
+ device = DexterDevice.create_ble()
136
+ device.connect()
137
+ device.start_acquisition()
138
+
139
+ processors = {}
140
+ for finger in Finger:
141
+ processors[finger] = ForceProcessor(
142
+ DEFAULT_CALIBRATION, get_default_orientation(finger)
143
+ )
144
+
145
+ recorder = DataRecorder(
146
+ "./recordings",
147
+ file_format=RecordingFormat.SQLITE,
148
+ metadata=RecordingMetadata(
149
+ device_name=device.get_device_name(),
150
+ sampling_rate_hz=device.get_sampling_rate().value,
151
+ ),
152
+ )
153
+
154
+ def on_sample(_data):
155
+ fingers = [device.finger_data[f] for f in Finger]
156
+ forces = [processors[f].process(device.finger_data[f]) for f in Finger]
157
+ baselines = [(processors[f].tare.baseline_x, processors[f].tare.baseline_y) for f in Finger]
158
+ recorder.write_sample(fingers, forces, baselines)
159
+
160
+ device.register_finger_callback(Finger.PINKY, on_sample)
161
+
162
+ recorder.start_recording()
163
+ # ... run until done ...
164
+ recorder.stop_recording()
165
+ device.close()
166
+ ```
167
+
168
+ Each recording file includes session metadata.
169
+
170
+ Session metadata has the following fields:
171
+
172
+ - `session_id`: Unique identifier for the recording session.
173
+ - `recording_started_at`: Timestamp when the recording started.
174
+ - `recording_stopped_at`: Timestamp when the recording stopped.
175
+ - `total_samples`: Total number of samples recorded.
176
+ - `library_version`: Version of the library used for recording.
177
+ - `timestamps`: Array of timestamps for each sample.
178
+ - `device_name`: Name of the device used for recording.
179
+ - `sampling_rate_hz`: Sampling rate in Hz.
180
+ - `extras`: Dictionary for experiment-specific metadata.
181
+
182
+ You can attach experiment-specific metadata via `RecordingMetadata.extras`:
183
+
184
+ ```python
185
+ metadata = RecordingMetadata(
186
+ device_name="Dexter-001",
187
+ sampling_rate_hz=1000,
188
+ extras={"subject_id": "S01", "trial": "3", "condition": "baseline"},
189
+ )
190
+ ```
191
+
192
+ ## Reading recordings
193
+
194
+ Load recorded sessions back for analysis:
195
+
196
+ ```python
197
+ from dexter_controller import RecordingReader
198
+
199
+ recording = RecordingReader.read("recordings/session.db")
200
+ print(f"Samples: {recording.num_rows}")
201
+ print(f"Session: {recording.metadata.session_id}")
202
+ print(f"Forces shape: {recording.forces.shape}") # (N, 10) - Fx,Fy per finger
203
+ print(f"Raw channels: {recording.raw_channels.shape}") # (N, 15) - 3 channels per finger
204
+ ```
205
+
206
+ For either the Sqlite and Parquet formats, you can use Pandas or Polars to load the data for analysis.
207
+
208
+ ```python
209
+ import pandas as pd
210
+
211
+ # for Sqlite format, use:
212
+ conn = RecordingReader.open("recordings/session.db")
213
+ df = pd.read_sql("SELECT * FROM samples", conn)
214
+ print(df.head())
215
+
216
+ # or for Parquet format, use:
217
+ df = pd.read_parquet("recordings/session.parquet")
218
+ print(df.head())
219
+ ```
220
+
221
+ You can also convert those formats to CSV for easier analysis or interoperability:
222
+
223
+ ```python
224
+ RecordingReader.to_csv("recordings/session.db") # produces session.csv
225
+ ```
226
+
227
+ ## BLE device configuration
228
+
229
+ When connected via BLE, you can read and write device settings:
230
+
231
+ ```python
232
+ from dexter_controller import SamplingRate
233
+
234
+ device.get_device_name() # read name
235
+ device.set_device_name("MyDexter") # rename device
236
+ device.get_sampling_rate() # current rate
237
+ device.set_sampling_rate(SamplingRate.HZ_1000) # change rate
238
+ device.get_battery_level() # battery percentage
239
+ ```
240
+
241
+ > [!WARNING]
242
+ > The Visualizer application requires that the device's name must include "Dexter" within the name (e.g., "MyDexter").
243
+
244
+ Available sampling rates: 100, 200, 400, 800, 1000, 2000 Hz.
245
+
246
+ ## Examples
247
+
248
+ See the [example/](example/) directory for complete, runnable scripts:
249
+
250
+ | File | Description |
251
+ | ------------------------------------------------------------ | --------------------------------------------------------------------------------- |
252
+ | [example_ble.py](example/example_ble.py) | BLE connection with raw data streaming and event rate monitoring |
253
+ | [example_ble_force.py](example/example_ble_force.py) | BLE connection with real-time force computation using `ForceProcessor` |
254
+ | [example_ble_recording.py](example/example_ble_recording.py) | Full recording session: BLE connect, force processing, and data recording to file |
255
+ | [example_uart.py](example/example_uart.py) | UART (wired) connection |
256
+
257
+ ## UART connection
258
+
259
+ For wired setups using UART:
260
+
261
+ ```python
262
+ import sys
263
+ from dexter_controller import DexterDevice, EventFlags, Finger, SamplingRate
264
+
265
+ device = DexterDevice.create_uart("COM3") # or /dev/ttyUSB0 on Linux
266
+
267
+ with device:
268
+ device.set_sampling_rate(SamplingRate.HZ_800)
269
+ device.set_event_enable(EventFlags.LOAD_CELL_DATA)
270
+ device.set_finger_channel_mapping([Finger.RING, None, None, None, None])
271
+ device.start_acquisition()
272
+ # ... read data via device.register_data_callback(callback)
273
+ ```
@@ -0,0 +1,248 @@
1
+ # dexter-controller
2
+
3
+ [![PyPI](https://img.shields.io/pypi/v/dexter-controller)](https://pypi.org/project/dexter-controller/)
4
+ [![Python](https://img.shields.io/pypi/pyversions/dexter-controller)](https://pypi.org/project/dexter-controller/)
5
+
6
+ Python library for the **Dexter** hand device. This library handles device communication, force computation, calibration, and data recording.
7
+
8
+ ## Installation
9
+
10
+ The recommended way to install the library is via `uv`. In your project add the dependency with:
11
+
12
+ ```bash
13
+ uv add dexter-controller
14
+ # or use pip
15
+ pip install dexter-controller
16
+ ```
17
+
18
+ Optional extras for reading and writing recordings:
19
+
20
+ ```bash
21
+ uv add dexter-controller[reader] # numpy, for loading recordings
22
+ uv add dexter-controller[parquet] # pyarrow + numpy, for Parquet format support
23
+ # or use pip
24
+ pip install dexter-controller[reader] # numpy, for loading recordings
25
+ pip install dexter-controller[parquet] # pyarrow + numpy, for Parquet format support
26
+ ```
27
+
28
+ ## Quick start
29
+
30
+ Connect over BLE or UART using the `DexterDevice` factory:
31
+
32
+ ```python
33
+ import time
34
+ from dexter_controller import DexterDevice, Finger
35
+
36
+ device = DexterDevice.create_ble(
37
+ # connect to a specific device by name:
38
+ # device_name="Dexter-001"
39
+ )
40
+ device.connect()
41
+ device.start_acquisition()
42
+
43
+ device_name = device.get_device_name()
44
+ battery = device.get_battery_level()
45
+ rate = device.get_sampling_rate()
46
+ print(f"Device: {device_name} | Battery: {battery}% | Rate: {rate.value} Hz")
47
+
48
+ try:
49
+ while True:
50
+ for finger in Finger:
51
+ data = device.finger_data[finger]
52
+ print(f"{finger.name}: {data.raw_data}", end=" ")
53
+ print(end="\r", flush=True)
54
+ time.sleep(0.02)
55
+ except KeyboardInterrupt:
56
+ pass
57
+ finally:
58
+ device.close()
59
+ ```
60
+
61
+ ## Force computation
62
+
63
+ Convert raw load-cell readings into calibrated 2D force vectors (Fx, Fy):
64
+
65
+ ```python
66
+ from dexter_controller import (
67
+ DEFAULT_CALIBRATION,
68
+ DexterDevice,
69
+ Finger,
70
+ ForceProcessor,
71
+ get_default_orientation,
72
+ )
73
+
74
+ device = DexterDevice.create_ble()
75
+ device.connect()
76
+ device.start_acquisition()
77
+
78
+ processors = {}
79
+ for finger in Finger:
80
+ orientation = get_default_orientation(finger)
81
+ processors[finger] = ForceProcessor(DEFAULT_CALIBRATION, orientation)
82
+
83
+ # In your read loop:
84
+ data = device.finger_data[Finger.INDEX]
85
+ fx, fy = processors[Finger.INDEX].process(data)
86
+ ```
87
+
88
+ Each `ForceProcessor` includes a `TareState` that lets you zero-out the baseline:
89
+
90
+ ```python
91
+ processors[Finger.INDEX].tare.start_tare(sample_count=20)
92
+ ```
93
+
94
+ ## Recording data
95
+
96
+ Record sessions to disk in CSV, SQLite (**default**), or Parquet format:
97
+
98
+ ```python
99
+ from dexter_controller import (
100
+ DEFAULT_CALIBRATION,
101
+ DataRecorder,
102
+ DexterDevice,
103
+ Finger,
104
+ ForceProcessor,
105
+ RecordingMetadata,
106
+ get_default_orientation,
107
+ )
108
+ from dexter_controller.recording_format import RecordingFormat
109
+
110
+ device = DexterDevice.create_ble()
111
+ device.connect()
112
+ device.start_acquisition()
113
+
114
+ processors = {}
115
+ for finger in Finger:
116
+ processors[finger] = ForceProcessor(
117
+ DEFAULT_CALIBRATION, get_default_orientation(finger)
118
+ )
119
+
120
+ recorder = DataRecorder(
121
+ "./recordings",
122
+ file_format=RecordingFormat.SQLITE,
123
+ metadata=RecordingMetadata(
124
+ device_name=device.get_device_name(),
125
+ sampling_rate_hz=device.get_sampling_rate().value,
126
+ ),
127
+ )
128
+
129
+ def on_sample(_data):
130
+ fingers = [device.finger_data[f] for f in Finger]
131
+ forces = [processors[f].process(device.finger_data[f]) for f in Finger]
132
+ baselines = [(processors[f].tare.baseline_x, processors[f].tare.baseline_y) for f in Finger]
133
+ recorder.write_sample(fingers, forces, baselines)
134
+
135
+ device.register_finger_callback(Finger.PINKY, on_sample)
136
+
137
+ recorder.start_recording()
138
+ # ... run until done ...
139
+ recorder.stop_recording()
140
+ device.close()
141
+ ```
142
+
143
+ Each recording file includes session metadata.
144
+
145
+ Session metadata has the following fields:
146
+
147
+ - `session_id`: Unique identifier for the recording session.
148
+ - `recording_started_at`: Timestamp when the recording started.
149
+ - `recording_stopped_at`: Timestamp when the recording stopped.
150
+ - `total_samples`: Total number of samples recorded.
151
+ - `library_version`: Version of the library used for recording.
152
+ - `timestamps`: Array of timestamps for each sample.
153
+ - `device_name`: Name of the device used for recording.
154
+ - `sampling_rate_hz`: Sampling rate in Hz.
155
+ - `extras`: Dictionary for experiment-specific metadata.
156
+
157
+ You can attach experiment-specific metadata via `RecordingMetadata.extras`:
158
+
159
+ ```python
160
+ metadata = RecordingMetadata(
161
+ device_name="Dexter-001",
162
+ sampling_rate_hz=1000,
163
+ extras={"subject_id": "S01", "trial": "3", "condition": "baseline"},
164
+ )
165
+ ```
166
+
167
+ ## Reading recordings
168
+
169
+ Load recorded sessions back for analysis:
170
+
171
+ ```python
172
+ from dexter_controller import RecordingReader
173
+
174
+ recording = RecordingReader.read("recordings/session.db")
175
+ print(f"Samples: {recording.num_rows}")
176
+ print(f"Session: {recording.metadata.session_id}")
177
+ print(f"Forces shape: {recording.forces.shape}") # (N, 10) - Fx,Fy per finger
178
+ print(f"Raw channels: {recording.raw_channels.shape}") # (N, 15) - 3 channels per finger
179
+ ```
180
+
181
+ For either the Sqlite and Parquet formats, you can use Pandas or Polars to load the data for analysis.
182
+
183
+ ```python
184
+ import pandas as pd
185
+
186
+ # for Sqlite format, use:
187
+ conn = RecordingReader.open("recordings/session.db")
188
+ df = pd.read_sql("SELECT * FROM samples", conn)
189
+ print(df.head())
190
+
191
+ # or for Parquet format, use:
192
+ df = pd.read_parquet("recordings/session.parquet")
193
+ print(df.head())
194
+ ```
195
+
196
+ You can also convert those formats to CSV for easier analysis or interoperability:
197
+
198
+ ```python
199
+ RecordingReader.to_csv("recordings/session.db") # produces session.csv
200
+ ```
201
+
202
+ ## BLE device configuration
203
+
204
+ When connected via BLE, you can read and write device settings:
205
+
206
+ ```python
207
+ from dexter_controller import SamplingRate
208
+
209
+ device.get_device_name() # read name
210
+ device.set_device_name("MyDexter") # rename device
211
+ device.get_sampling_rate() # current rate
212
+ device.set_sampling_rate(SamplingRate.HZ_1000) # change rate
213
+ device.get_battery_level() # battery percentage
214
+ ```
215
+
216
+ > [!WARNING]
217
+ > The Visualizer application requires that the device's name must include "Dexter" within the name (e.g., "MyDexter").
218
+
219
+ Available sampling rates: 100, 200, 400, 800, 1000, 2000 Hz.
220
+
221
+ ## Examples
222
+
223
+ See the [example/](example/) directory for complete, runnable scripts:
224
+
225
+ | File | Description |
226
+ | ------------------------------------------------------------ | --------------------------------------------------------------------------------- |
227
+ | [example_ble.py](example/example_ble.py) | BLE connection with raw data streaming and event rate monitoring |
228
+ | [example_ble_force.py](example/example_ble_force.py) | BLE connection with real-time force computation using `ForceProcessor` |
229
+ | [example_ble_recording.py](example/example_ble_recording.py) | Full recording session: BLE connect, force processing, and data recording to file |
230
+ | [example_uart.py](example/example_uart.py) | UART (wired) connection |
231
+
232
+ ## UART connection
233
+
234
+ For wired setups using UART:
235
+
236
+ ```python
237
+ import sys
238
+ from dexter_controller import DexterDevice, EventFlags, Finger, SamplingRate
239
+
240
+ device = DexterDevice.create_uart("COM3") # or /dev/ttyUSB0 on Linux
241
+
242
+ with device:
243
+ device.set_sampling_rate(SamplingRate.HZ_800)
244
+ device.set_event_enable(EventFlags.LOAD_CELL_DATA)
245
+ device.set_finger_channel_mapping([Finger.RING, None, None, None, None])
246
+ device.start_acquisition()
247
+ # ... read data via device.register_data_callback(callback)
248
+ ```
@@ -0,0 +1,37 @@
1
+ [project]
2
+ name = "dexter-controller"
3
+ version = "1.0.0a1"
4
+ description = "Dexter device Controller"
5
+ readme = "README.md"
6
+ requires-python = ">=3.11"
7
+ classifiers = [
8
+ "Programming Language :: Python :: 3",
9
+ "Programming Language :: Python :: 3.11",
10
+ "Programming Language :: Python :: 3.12",
11
+ "Programming Language :: Python :: 3.13",
12
+ ]
13
+ dependencies = [
14
+ "bleak==2.1.1",
15
+ "harp-protocol==0.5.0",
16
+ "harp-serial==0.5.0",
17
+ ]
18
+
19
+ [[project.authors]]
20
+ name = "Hardware and Software Platform, Champalimaud Foundation"
21
+ email = "software@research.fchampalimaud.org"
22
+
23
+ [project.optional-dependencies]
24
+ reader = ["numpy>=1.25.0"]
25
+ parquet = [
26
+ "pyarrow>=14.0.0",
27
+ "dexter-controller[reader]",
28
+ ]
29
+ docs = ["pydoc-markdown>=4.8.2"]
30
+
31
+ [project.urls]
32
+ Repository = "https://github.com/fchampalimaud/dexter-controller/"
33
+ "Bug Tracker" = "https://github.com/fchampalimaud/dexter-controller/issues"
34
+
35
+ [build-system]
36
+ requires = ["uv_build>=0.9.5,<0.13.0"]
37
+ build-backend = "uv_build"
@@ -0,0 +1,35 @@
1
+ [project]
2
+ name = "dexter-controller"
3
+ version = "1.0.0a1"
4
+ description = "Dexter device Controller"
5
+ readme = "README.md"
6
+ authors = [
7
+ { name = "Hardware and Software Platform, Champalimaud Foundation", email = "software@research.fchampalimaud.org" }
8
+ ]
9
+ requires-python = ">=3.11"
10
+ classifiers = [
11
+ "Programming Language :: Python :: 3",
12
+ "Programming Language :: Python :: 3.11",
13
+ "Programming Language :: Python :: 3.12",
14
+ "Programming Language :: Python :: 3.13",
15
+ ]
16
+ dependencies = [
17
+ "bleak==2.1.1",
18
+ "harp-protocol==0.5.0",
19
+ "harp-serial==0.5.0",
20
+ ]
21
+
22
+ [project.optional-dependencies]
23
+ reader = ["numpy>=1.25.0"]
24
+ parquet = ["pyarrow>=14.0.0", "dexter-controller[reader]"]
25
+ docs = [
26
+ "pydoc-markdown>=4.8.2",
27
+ ]
28
+
29
+ [project.urls]
30
+ Repository = "https://github.com/fchampalimaud/dexter-controller/"
31
+ "Bug Tracker" = "https://github.com/fchampalimaud/dexter-controller/issues"
32
+
33
+ [build-system]
34
+ requires = ["uv_build>=0.9.5,<0.13.0"]
35
+ build-backend = "uv_build"
@@ -0,0 +1,58 @@
1
+ from .ble_dexter_device import BLEDexterDevice
2
+ from .calibration import (
3
+ DEFAULT_CALIBRATION,
4
+ CalibrationProfile,
5
+ ChannelCalibration,
6
+ LoadCellCalibration,
7
+ LoadCellCoefficients,
8
+ calibration_from_channel,
9
+ create_calibration,
10
+ )
11
+ from .data_recorder import DataRecorder
12
+ from .dexter_device import (
13
+ DataMode,
14
+ DexterDevice,
15
+ ErrorStatus,
16
+ EventFlags,
17
+ HandSelection,
18
+ SamplingRate,
19
+ )
20
+ from .finger import Finger, FingerData
21
+ from .finger_orientation import FingerOrientation
22
+ from .finger_orientation import get_default as get_default_orientation
23
+ from .force_processor import ForceProcessor
24
+ from .recording import Recording
25
+ from .recording_format import RecordingFormat
26
+ from .recording_metadata import RecordingMetadata
27
+ from .recording_reader import RecordingReader
28
+ from .tare import TareState
29
+ from .uart_dexter_device import UartDexterDevice
30
+
31
+ __all__ = [
32
+ "DEFAULT_CALIBRATION",
33
+ "BLEDexterDevice",
34
+ "CalibrationProfile",
35
+ "ChannelCalibration",
36
+ "DataMode",
37
+ "DataRecorder",
38
+ "DexterDevice",
39
+ "ErrorStatus",
40
+ "EventFlags",
41
+ "Finger",
42
+ "FingerData",
43
+ "FingerOrientation",
44
+ "ForceProcessor",
45
+ "HandSelection",
46
+ "LoadCellCalibration",
47
+ "LoadCellCoefficients",
48
+ "Recording",
49
+ "RecordingFormat",
50
+ "RecordingMetadata",
51
+ "RecordingReader",
52
+ "SamplingRate",
53
+ "TareState",
54
+ "UartDexterDevice",
55
+ "calibration_from_channel",
56
+ "create_calibration",
57
+ "get_default_orientation",
58
+ ]