standardbots 2.0.0.dev1737138088__tar.gz → 2.0.0.dev1737492400__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.

Potentially problematic release.


This version of standardbots might be problematic. Click here for more details.

Files changed (17) hide show
  1. {standardbots-2.0.0.dev1737138088 → standardbots-2.0.0.dev1737492400}/PKG-INFO +1 -1
  2. standardbots-2.0.0.dev1737492400/README.md +141 -0
  3. {standardbots-2.0.0.dev1737138088 → standardbots-2.0.0.dev1737492400}/setup.py +1 -1
  4. {standardbots-2.0.0.dev1737138088 → standardbots-2.0.0.dev1737492400}/standardbots/auto_generated/apis.py +2 -2
  5. {standardbots-2.0.0.dev1737138088 → standardbots-2.0.0.dev1737492400}/standardbots/auto_generated/models.py +213 -151
  6. {standardbots-2.0.0.dev1737138088 → standardbots-2.0.0.dev1737492400}/standardbots.egg-info/PKG-INFO +1 -1
  7. {standardbots-2.0.0.dev1737138088 → standardbots-2.0.0.dev1737492400}/standardbots.egg-info/SOURCES.txt +6 -1
  8. {standardbots-2.0.0.dev1737138088 → standardbots-2.0.0.dev1737492400}/standardbots.egg-info/top_level.txt +1 -0
  9. standardbots-2.0.0.dev1737492400/tests/fixtures/__init__.py +0 -0
  10. standardbots-2.0.0.dev1737492400/tests/fixtures/client_fixt.py +25 -0
  11. standardbots-2.0.0.dev1737492400/tests/fixtures/routines_fixt.py +48 -0
  12. standardbots-2.0.0.dev1737492400/tests/test_apis.py +1294 -0
  13. {standardbots-2.0.0.dev1737138088 → standardbots-2.0.0.dev1737492400}/setup.cfg +0 -0
  14. {standardbots-2.0.0.dev1737138088 → standardbots-2.0.0.dev1737492400}/standardbots/__init__.py +0 -0
  15. {standardbots-2.0.0.dev1737138088 → standardbots-2.0.0.dev1737492400}/standardbots/auto_generated/__init__.py +0 -0
  16. {standardbots-2.0.0.dev1737138088 → standardbots-2.0.0.dev1737492400}/standardbots.egg-info/dependency_links.txt +0 -0
  17. {standardbots-2.0.0.dev1737138088 → standardbots-2.0.0.dev1737492400}/standardbots.egg-info/requires.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: standardbots
3
- Version: 2.0.0.dev1737138088
3
+ Version: 2.0.0.dev1737492400
4
4
  Summary: Standard Bots RO1 Robotics API
5
5
  Home-page:
6
6
  Author: Standard Bots Support
@@ -0,0 +1,141 @@
1
+ You can test the API by using the playground, add your own file or use the existing python scripts.
2
+ When a client has an issue, and they send their code. It's helpful to test their code to see possible issues
3
+
4
+ # ENABLE API ON YOUR LOCAL MACHINE
5
+
6
+ Follow the steps here https://www.notion.so/standardbots/Using-the-REST-API-b2c778d47969444dac61483f0117acad
7
+
8
+ # CONFIG
9
+
10
+ At the top of the file, use your token
11
+
12
+ # RUN
13
+
14
+ To run a script move into the `sdks/python` folder and run `python playground/filename.py`
15
+
16
+ # To create a test build
17
+
18
+ 1. Update version in `setup.py`
19
+ 2. Run `python3 setup.py sdist bdist_wheel`
20
+
21
+ # Tests
22
+
23
+ ## Setup
24
+
25
+ To set up tests:
26
+
27
+ ```bash
28
+ cd sdks/python
29
+
30
+ pip install -r requirements.txt
31
+ pip install -r requirements-dev.txt
32
+ ```
33
+
34
+ ### Create sample data
35
+
36
+ #### Sample routine
37
+
38
+ You need to add the sample routine in [sdks/python/tests/fixtures/test_public_api_routine.json](./tests/fixtures/test_public_api_routine.json) to your target test environment (i.e. upload the routine to ).
39
+
40
+ The name of routine should be "Test Public API"
41
+
42
+ #### Sample globals
43
+
44
+ - _Variable._ Add global variable called "test_public_api_global" with any value.
45
+ - _Space._ Create a global space called "Test global space" of any kind.
46
+
47
+
48
+ ## Running
49
+
50
+ Here is a basic test command:
51
+
52
+ ```bash
53
+ SB_API_URL=http://34.162.0.32:3000
54
+ SB_API_TOKEN=...
55
+
56
+ python3 -m pytest ./tests --cov=standardbots --token=$SB_API_TOKEN --api-url=$SB_API_URL
57
+ ```
58
+
59
+ You may also set up a `.env` file at `sdks/python/.env` with the following contents:
60
+
61
+ ```bash
62
+ export SB_API_URL=http://34.162.0.32:3000
63
+ export SB_API_TOKEN=...
64
+ ```
65
+
66
+ Then you can just do:
67
+
68
+ ```bash
69
+ python3 -m pytest ./tests --cov=standardbots
70
+ ```
71
+
72
+ ### Robot state and testing (Markers)
73
+
74
+ We need the bot to be in a certain state to run certain tests. For example, we need a routine to be running in order to stop the routine.
75
+
76
+ At start of testing, robot should:
77
+
78
+ - _NOT_ be e-stopped.
79
+
80
+
81
+ The basic idea here is:
82
+
83
+ - These special tests will not be run by default.
84
+ - You may pass a flag (e.g. `--routine-running`) when the bot is in the correct state to run the tests.
85
+ - When the flag is passed:
86
+ 1. Tests with the flag are run.
87
+ 2. Tests without the flag are not run.
88
+
89
+ We use [pytest markers](https://docs.pytest.org/en/7.1.x/example/markers.html) to do this.
90
+
91
+ #### Routine running
92
+
93
+ The special sample routine ("Test Public API") should be running prior to running these tests. Then do:
94
+
95
+ ```bash
96
+ python3 -m pytest ./tests --cov=standardbots --routine-running
97
+ ```
98
+
99
+ #### E-stop
100
+
101
+ No marker needed for e-stop. However, we do rely on active recovery of e-stop and getting the failure state in order to do these tests.
102
+
103
+ When e-stop test runs, cannot have bot in a failure state (pre-test will fail).
104
+
105
+ ## Troubleshooting
106
+
107
+ ### Tests are hanging
108
+
109
+ The first test appears to start but then nothing happens for several seconds:
110
+
111
+ ```bash
112
+ $ python3 -m pytest ./tests --cov=standardbots
113
+ ========================================================================================================== test session starts ===========================================================================================================
114
+ platform linux -- Python 3.10.12, pytest-6.2.5, py-1.10.0, pluggy-0.13.0
115
+ rootdir: /workspaces/sb/sdks/python, configfile: pytest.ini
116
+ plugins: ament-pep257-0.12.11, ament-xmllint-0.12.11, launch-testing-1.0.6, launch-testing-ros-0.19.7, ament-flake8-0.12.11, ament-lint-0.12.11, ament-copyright-0.12.11, colcon-core-0.18.1, cov-6.0.0
117
+ collected 110 items
118
+
119
+ tests/test_apis.py
120
+ ```
121
+
122
+ Fixes:
123
+
124
+ - _Make sure you can log into remote control._ Ensure that botman is connected.
125
+ - _Ensure that the robot URL is up-to-date._ Botman url will often change when you reboot.
126
+
127
+ ### Custom sensors
128
+
129
+ To test custom sensors:
130
+ - go to the menu on the left bottom corner;
131
+ - Click on 'Equipment';
132
+ - Add Gripper > Custom Gripper;
133
+ - Go to the Sensors tab and click 'Add Sensor';
134
+ - Keep the default values as they are (name: 'Sensor 1', kind: 'Control Box IO', sensor value: 'low');
135
+ - Hit 'Save' and make sure the Custom Gripper is enabled.
136
+
137
+ Then run:
138
+
139
+ ```bash
140
+ python3 -m pytest ./tests --cov=standardbots --custom-sensors
141
+ ```
@@ -13,7 +13,7 @@
13
13
  from setuptools import setup, find_packages # noqa: H301
14
14
 
15
15
  NAME = "standardbots"
16
- VERSION = "2.0.0-dev1737138088"
16
+ VERSION = "2.0.0-dev1737492400"
17
17
  # To install the library, run the following
18
18
  #
19
19
  # python setup.py install
@@ -188,7 +188,7 @@ class Default:
188
188
  return self.control_gripper(
189
189
  body=models.GripperCommandRequest(
190
190
  kind=models.GripperKindEnum.DhCgi,
191
- dh_pgc=models.DHCGIGripperCommandRequest(
191
+ dh_cgi=models.DHCGIGripperCommandRequest(
192
192
  target_diameter, target_force, target_speed
193
193
  ),
194
194
  ),
@@ -713,7 +713,7 @@ class Camera:
713
713
  None
714
714
  ]:
715
715
  """
716
- Retrieve the latest RGB frame from the camera.
716
+ Retrieve the latest RGB frame from the camera. In JPEG format.
717
717
  """
718
718
  path = "/api/v1/camera/frame/rgb"
719
719
  try:
@@ -1087,6 +1087,8 @@ class ErrorEnum(Enum):
1087
1087
  """Requested resource not found"""
1088
1088
  InvalidSpaceSpecified = "invalid_space_specified"
1089
1089
  """Space specified was invalid or not found"""
1090
+ InvalidParameters = "invalid_parameters"
1091
+ """Parameters are invalid"""
1090
1092
 
1091
1093
  def parse_error_enum(data: object) -> ErrorEnum:
1092
1094
  return ErrorEnum(data)
@@ -1372,6 +1374,66 @@ def parse_joint_rotations(data: object) -> JointRotations:
1372
1374
  def serialize_joint_rotations(data: JointRotations) -> object:
1373
1375
  return [serialize_f_64(data[0]),serialize_f_64(data[1]),serialize_f_64(data[2]),serialize_f_64(data[3]),serialize_f_64(data[4]),serialize_f_64(data[5]),]
1374
1376
 
1377
+ @dataclass
1378
+ class JointStateDisturbance:
1379
+ """Disturbance of a joint"""
1380
+ disturbance: Union[float, None] = None
1381
+ amperageDisturbance: Union[float, None] = None
1382
+ windupDisturbance: Union[float, None] = None
1383
+
1384
+ def validate_disturbance(self, value: float) -> Tuple[bool, str]:
1385
+ if value is None:
1386
+ return [True, ""]
1387
+
1388
+ if not isinstance(value, float):
1389
+ return [False, "disturbance must be of type float for JointStateDisturbance, got " + type(value).__name__]
1390
+
1391
+ return [True, ""]
1392
+
1393
+ def validate_amperageDisturbance(self, value: float) -> Tuple[bool, str]:
1394
+ if value is None:
1395
+ return [True, ""]
1396
+
1397
+ if not isinstance(value, float):
1398
+ return [False, "amperageDisturbance must be of type float for JointStateDisturbance, got " + type(value).__name__]
1399
+
1400
+ return [True, ""]
1401
+
1402
+ def validate_windupDisturbance(self, value: float) -> Tuple[bool, str]:
1403
+ if value is None:
1404
+ return [True, ""]
1405
+
1406
+ if not isinstance(value, float):
1407
+ return [False, "windupDisturbance must be of type float for JointStateDisturbance, got " + type(value).__name__]
1408
+
1409
+ return [True, ""]
1410
+
1411
+ def __post_init__(self):
1412
+ # Type check incoming model - raise error if invalid (required or wrong type)
1413
+ is_valid, error_str = self.validate_disturbance(self.disturbance)
1414
+ if not is_valid:
1415
+ raise TypeError(error_str)
1416
+ is_valid, error_str = self.validate_amperageDisturbance(self.amperageDisturbance)
1417
+ if not is_valid:
1418
+ raise TypeError(error_str)
1419
+ is_valid, error_str = self.validate_windupDisturbance(self.windupDisturbance)
1420
+ if not is_valid:
1421
+ raise TypeError(error_str)
1422
+
1423
+ def parse_joint_state_disturbance(data: object):
1424
+ return JointStateDisturbance(
1425
+ disturbance=parse_f_64(data["disturbance"]) if "disturbance" in data and data.get("disturbance") is not None else None,
1426
+ amperageDisturbance=parse_f_64(data["amperageDisturbance"]) if "amperageDisturbance" in data and data.get("amperageDisturbance") is not None else None,
1427
+ windupDisturbance=parse_f_64(data["windupDisturbance"]) if "windupDisturbance" in data and data.get("windupDisturbance") is not None else None,
1428
+ )
1429
+
1430
+ def serialize_joint_state_disturbance(data: JointStateDisturbance) -> object:
1431
+ return {
1432
+ "disturbance": None if data.disturbance is None else serialize_f_64(data.disturbance),
1433
+ "amperageDisturbance": None if data.amperageDisturbance is None else serialize_f_64(data.amperageDisturbance),
1434
+ "windupDisturbance": None if data.windupDisturbance is None else serialize_f_64(data.windupDisturbance),
1435
+ }
1436
+
1375
1437
  class LinearGripDirectionEnum(Enum):
1376
1438
  Inward = "inward"
1377
1439
  """Move gripper inward to grip. Measure grip position based on interior of gripper fingers and exterior of object"""
@@ -2655,7 +2717,7 @@ def serialize_camera_intrinsics_response(data: CameraIntrinsicsResponse) -> obje
2655
2717
 
2656
2718
  @dataclass
2657
2719
  class CameraFrameRequest:
2658
- """Request for a single camera frame."""
2720
+ """Request for a single camera frame. In JPEG format."""
2659
2721
  camera_settings: Union[CameraSettings, None] = None
2660
2722
 
2661
2723
  def validate_camera_settings(self, value: CameraSettings) -> Tuple[bool, str]:
@@ -2959,81 +3021,6 @@ def serialize_tooltip_position_response(data: TooltipPositionResponse) -> object
2959
3021
  "pose": None if data.pose is None else serialize_cartesian_pose(data.pose),
2960
3022
  }
2961
3023
 
2962
- @dataclass
2963
- class JointState:
2964
- """State of a joint"""
2965
- braked: Union[bool, None] = None
2966
- connectionStatus: Union[ConnectionStatus, None] = None
2967
- inCollision: Union[bool, None] = None
2968
- disturbance: Union[float, None] = None
2969
-
2970
- def validate_braked(self, value: bool) -> Tuple[bool, str]:
2971
- if value is None:
2972
- return [True, ""]
2973
-
2974
- if not isinstance(value, bool):
2975
- return [False, "braked must be of type bool for JointState, got " + type(value).__name__]
2976
-
2977
- return [True, ""]
2978
-
2979
- def validate_connectionStatus(self, value: ConnectionStatus) -> Tuple[bool, str]:
2980
- if value is None:
2981
- return [True, ""]
2982
-
2983
- if not ((isinstance(value, str) and ConnectionStatus in ['connected', 'disconnected', 'ready']) or isinstance(value, ConnectionStatus)):
2984
- return [False, "connectionStatus must be of type ConnectionStatus for JointState, got " + type(value).__name__]
2985
-
2986
- return [True, ""]
2987
-
2988
- def validate_inCollision(self, value: bool) -> Tuple[bool, str]:
2989
- if value is None:
2990
- return [True, ""]
2991
-
2992
- if not isinstance(value, bool):
2993
- return [False, "inCollision must be of type bool for JointState, got " + type(value).__name__]
2994
-
2995
- return [True, ""]
2996
-
2997
- def validate_disturbance(self, value: float) -> Tuple[bool, str]:
2998
- if value is None:
2999
- return [True, ""]
3000
-
3001
- if not isinstance(value, float):
3002
- return [False, "disturbance must be of type float for JointState, got " + type(value).__name__]
3003
-
3004
- return [True, ""]
3005
-
3006
- def __post_init__(self):
3007
- # Type check incoming model - raise error if invalid (required or wrong type)
3008
- is_valid, error_str = self.validate_braked(self.braked)
3009
- if not is_valid:
3010
- raise TypeError(error_str)
3011
- is_valid, error_str = self.validate_connectionStatus(self.connectionStatus)
3012
- if not is_valid:
3013
- raise TypeError(error_str)
3014
- is_valid, error_str = self.validate_inCollision(self.inCollision)
3015
- if not is_valid:
3016
- raise TypeError(error_str)
3017
- is_valid, error_str = self.validate_disturbance(self.disturbance)
3018
- if not is_valid:
3019
- raise TypeError(error_str)
3020
-
3021
- def parse_joint_state(data: object):
3022
- return JointState(
3023
- braked=parse_bool(data["braked"]) if "braked" in data and data.get("braked") is not None else None,
3024
- connectionStatus=parse_connection_status(data["connectionStatus"]) if "connectionStatus" in data and data.get("connectionStatus") is not None else None,
3025
- inCollision=parse_bool(data["inCollision"]) if "inCollision" in data and data.get("inCollision") is not None else None,
3026
- disturbance=parse_f_64(data["disturbance"]) if "disturbance" in data and data.get("disturbance") is not None else None,
3027
- )
3028
-
3029
- def serialize_joint_state(data: JointState) -> object:
3030
- return {
3031
- "braked": None if data.braked is None else serialize_bool(data.braked),
3032
- "connectionStatus": None if data.connectionStatus is None else serialize_connection_status(data.connectionStatus),
3033
- "inCollision": None if data.inCollision is None else serialize_bool(data.inCollision),
3034
- "disturbance": None if data.disturbance is None else serialize_f_64(data.disturbance),
3035
- }
3036
-
3037
3024
  EnvironmentVariablesList = List[EnvironmentVariable]
3038
3025
 
3039
3026
  def parse_environment_variables_list(data: object) -> EnvironmentVariablesList:
@@ -3052,7 +3039,7 @@ class ErrorResponse:
3052
3039
  if value is None:
3053
3040
  return [False, "error is required for ErrorResponse"]
3054
3041
 
3055
- if not ((isinstance(value, str) and ErrorEnum in ['authorization_required', 'routine_must_be_running', 'api_control_required', 'robot_brakes_disengage_failed', 'robot_brakes_engage_failed', 'request_failed_validation', 'robot_not_idle', 'brakes_must_be_engaged', 'brakes_must_be_disengaged', 'equipment_no_matching', 'service_initializing', 'camera_disconnected', 'settings_validation_error', 'settings_timeout', 'internal_server_error', 'recovery_error', 'not_found', 'invalid_space_specified']) or isinstance(value, ErrorEnum)):
3042
+ if not ((isinstance(value, str) and ErrorEnum in ['authorization_required', 'routine_must_be_running', 'api_control_required', 'robot_brakes_disengage_failed', 'robot_brakes_engage_failed', 'request_failed_validation', 'robot_not_idle', 'brakes_must_be_engaged', 'brakes_must_be_disengaged', 'equipment_no_matching', 'service_initializing', 'camera_disconnected', 'settings_validation_error', 'settings_timeout', 'internal_server_error', 'recovery_error', 'not_found', 'invalid_space_specified', 'invalid_parameters']) or isinstance(value, ErrorEnum)):
3056
3043
  return [False, "error must be of type ErrorEnum for ErrorResponse, got " + type(value).__name__]
3057
3044
 
3058
3045
  return [True, ""]
@@ -3345,6 +3332,81 @@ def serialize_arm_joint_rotations(data: ArmJointRotations) -> object:
3345
3332
  "joints": serialize_joint_rotations(data.joints),
3346
3333
  }
3347
3334
 
3335
+ @dataclass
3336
+ class JointState:
3337
+ """State of a joint"""
3338
+ braked: Union[bool, None] = None
3339
+ connectionStatus: Union[ConnectionStatus, None] = None
3340
+ inCollision: Union[bool, None] = None
3341
+ disturbance: Union[JointStateDisturbance, None] = None
3342
+
3343
+ def validate_braked(self, value: bool) -> Tuple[bool, str]:
3344
+ if value is None:
3345
+ return [True, ""]
3346
+
3347
+ if not isinstance(value, bool):
3348
+ return [False, "braked must be of type bool for JointState, got " + type(value).__name__]
3349
+
3350
+ return [True, ""]
3351
+
3352
+ def validate_connectionStatus(self, value: ConnectionStatus) -> Tuple[bool, str]:
3353
+ if value is None:
3354
+ return [True, ""]
3355
+
3356
+ if not ((isinstance(value, str) and ConnectionStatus in ['connected', 'disconnected', 'ready']) or isinstance(value, ConnectionStatus)):
3357
+ return [False, "connectionStatus must be of type ConnectionStatus for JointState, got " + type(value).__name__]
3358
+
3359
+ return [True, ""]
3360
+
3361
+ def validate_inCollision(self, value: bool) -> Tuple[bool, str]:
3362
+ if value is None:
3363
+ return [True, ""]
3364
+
3365
+ if not isinstance(value, bool):
3366
+ return [False, "inCollision must be of type bool for JointState, got " + type(value).__name__]
3367
+
3368
+ return [True, ""]
3369
+
3370
+ def validate_disturbance(self, value: JointStateDisturbance) -> Tuple[bool, str]:
3371
+ if value is None:
3372
+ return [True, ""]
3373
+
3374
+ if not isinstance(value, JointStateDisturbance):
3375
+ return [False, "disturbance must be of type JointStateDisturbance for JointState, got " + type(value).__name__]
3376
+
3377
+ return [True, ""]
3378
+
3379
+ def __post_init__(self):
3380
+ # Type check incoming model - raise error if invalid (required or wrong type)
3381
+ is_valid, error_str = self.validate_braked(self.braked)
3382
+ if not is_valid:
3383
+ raise TypeError(error_str)
3384
+ is_valid, error_str = self.validate_connectionStatus(self.connectionStatus)
3385
+ if not is_valid:
3386
+ raise TypeError(error_str)
3387
+ is_valid, error_str = self.validate_inCollision(self.inCollision)
3388
+ if not is_valid:
3389
+ raise TypeError(error_str)
3390
+ is_valid, error_str = self.validate_disturbance(self.disturbance)
3391
+ if not is_valid:
3392
+ raise TypeError(error_str)
3393
+
3394
+ def parse_joint_state(data: object):
3395
+ return JointState(
3396
+ braked=parse_bool(data["braked"]) if "braked" in data and data.get("braked") is not None else None,
3397
+ connectionStatus=parse_connection_status(data["connectionStatus"]) if "connectionStatus" in data and data.get("connectionStatus") is not None else None,
3398
+ inCollision=parse_bool(data["inCollision"]) if "inCollision" in data and data.get("inCollision") is not None else None,
3399
+ disturbance=parse_joint_state_disturbance(data["disturbance"]) if "disturbance" in data and data.get("disturbance") is not None else None,
3400
+ )
3401
+
3402
+ def serialize_joint_state(data: JointState) -> object:
3403
+ return {
3404
+ "braked": None if data.braked is None else serialize_bool(data.braked),
3405
+ "connectionStatus": None if data.connectionStatus is None else serialize_connection_status(data.connectionStatus),
3406
+ "inCollision": None if data.inCollision is None else serialize_bool(data.inCollision),
3407
+ "disturbance": None if data.disturbance is None else serialize_joint_state_disturbance(data.disturbance),
3408
+ }
3409
+
3348
3410
  @dataclass
3349
3411
  class LinearUnit:
3350
3412
  """Reusable Abstraction for linear units (eg distance, position, offset)
@@ -4863,6 +4925,74 @@ def serialize_arm_position_update_request_response_event_stream_details(data: Ar
4863
4925
  "subscriptions": serialize_arm_position_update_request_response_event_stream_subscriptions_list(data.subscriptions),
4864
4926
  }
4865
4927
 
4928
+ @dataclass
4929
+ class Routine:
4930
+ """Robot Routine containing steps to automate robot movement and operations"""
4931
+ id: Union[str, None] = None
4932
+ name: Union[str, None] = None
4933
+ environment_variables: Union[EnvironmentVariablesList, None] = None
4934
+
4935
+ def validate_id(self, value: str) -> Tuple[bool, str]:
4936
+ if value is None:
4937
+ return [True, ""]
4938
+
4939
+ if not isinstance(value, str):
4940
+ return [False, "id must be of type str for Routine, got " + type(value).__name__]
4941
+
4942
+ return [True, ""]
4943
+
4944
+ def validate_name(self, value: str) -> Tuple[bool, str]:
4945
+ if value is None:
4946
+ return [True, ""]
4947
+
4948
+ if not isinstance(value, str):
4949
+ return [False, "name must be of type str for Routine, got " + type(value).__name__]
4950
+
4951
+ return [True, ""]
4952
+
4953
+ def validate_environment_variables(self, value: EnvironmentVariablesList) -> Tuple[bool, str]:
4954
+ if value is None:
4955
+ return [True, ""]
4956
+
4957
+ if not (isinstance(value, list) and all(isinstance(x, EnvironmentVariable) for x in value)):
4958
+ return [False, "environment_variables must be of type EnvironmentVariablesList for Routine, got " + type(value).__name__]
4959
+
4960
+ return [True, ""]
4961
+
4962
+ def __post_init__(self):
4963
+ # Type check incoming model - raise error if invalid (required or wrong type)
4964
+ is_valid, error_str = self.validate_id(self.id)
4965
+ if not is_valid:
4966
+ raise TypeError(error_str)
4967
+ is_valid, error_str = self.validate_name(self.name)
4968
+ if not is_valid:
4969
+ raise TypeError(error_str)
4970
+ is_valid, error_str = self.validate_environment_variables(self.environment_variables)
4971
+ if not is_valid:
4972
+ raise TypeError(error_str)
4973
+
4974
+ def parse_routine(data: object):
4975
+ return Routine(
4976
+ id=parse_str(data["id"]) if "id" in data and data.get("id") is not None else None,
4977
+ name=parse_str(data["name"]) if "name" in data and data.get("name") is not None else None,
4978
+ environment_variables=parse_environment_variables_list(data["environment_variables"]) if "environment_variables" in data and data.get("environment_variables") is not None else None,
4979
+ )
4980
+
4981
+ def serialize_routine(data: Routine) -> object:
4982
+ return {
4983
+ "id": None if data.id is None else serialize_str(data.id),
4984
+ "name": None if data.name is None else serialize_str(data.name),
4985
+ "environment_variables": None if data.environment_variables is None else serialize_environment_variables_list(data.environment_variables),
4986
+ }
4987
+
4988
+ ArmJointRotationsList = List[ArmJointRotations]
4989
+
4990
+ def parse_arm_joint_rotations_list(data: object) -> ArmJointRotationsList:
4991
+ return [parse_arm_joint_rotations(item) for item in data]
4992
+
4993
+ def serialize_arm_joint_rotations_list(data: ArmJointRotationsList) -> List[object]:
4994
+ return [serialize_arm_joint_rotations(item) for item in data]
4995
+
4866
4996
  @dataclass
4867
4997
  class JointsStateResponse:
4868
4998
  """Response to a query for the current state of the joints"""
@@ -4968,74 +5098,6 @@ def serialize_joints_state_response(data: JointsStateResponse) -> object:
4968
5098
  "J5": None if data.J5 is None else serialize_joint_state(data.J5),
4969
5099
  }
4970
5100
 
4971
- @dataclass
4972
- class Routine:
4973
- """Robot Routine containing steps to automate robot movement and operations"""
4974
- id: Union[str, None] = None
4975
- name: Union[str, None] = None
4976
- environment_variables: Union[EnvironmentVariablesList, None] = None
4977
-
4978
- def validate_id(self, value: str) -> Tuple[bool, str]:
4979
- if value is None:
4980
- return [True, ""]
4981
-
4982
- if not isinstance(value, str):
4983
- return [False, "id must be of type str for Routine, got " + type(value).__name__]
4984
-
4985
- return [True, ""]
4986
-
4987
- def validate_name(self, value: str) -> Tuple[bool, str]:
4988
- if value is None:
4989
- return [True, ""]
4990
-
4991
- if not isinstance(value, str):
4992
- return [False, "name must be of type str for Routine, got " + type(value).__name__]
4993
-
4994
- return [True, ""]
4995
-
4996
- def validate_environment_variables(self, value: EnvironmentVariablesList) -> Tuple[bool, str]:
4997
- if value is None:
4998
- return [True, ""]
4999
-
5000
- if not (isinstance(value, list) and all(isinstance(x, EnvironmentVariable) for x in value)):
5001
- return [False, "environment_variables must be of type EnvironmentVariablesList for Routine, got " + type(value).__name__]
5002
-
5003
- return [True, ""]
5004
-
5005
- def __post_init__(self):
5006
- # Type check incoming model - raise error if invalid (required or wrong type)
5007
- is_valid, error_str = self.validate_id(self.id)
5008
- if not is_valid:
5009
- raise TypeError(error_str)
5010
- is_valid, error_str = self.validate_name(self.name)
5011
- if not is_valid:
5012
- raise TypeError(error_str)
5013
- is_valid, error_str = self.validate_environment_variables(self.environment_variables)
5014
- if not is_valid:
5015
- raise TypeError(error_str)
5016
-
5017
- def parse_routine(data: object):
5018
- return Routine(
5019
- id=parse_str(data["id"]) if "id" in data and data.get("id") is not None else None,
5020
- name=parse_str(data["name"]) if "name" in data and data.get("name") is not None else None,
5021
- environment_variables=parse_environment_variables_list(data["environment_variables"]) if "environment_variables" in data and data.get("environment_variables") is not None else None,
5022
- )
5023
-
5024
- def serialize_routine(data: Routine) -> object:
5025
- return {
5026
- "id": None if data.id is None else serialize_str(data.id),
5027
- "name": None if data.name is None else serialize_str(data.name),
5028
- "environment_variables": None if data.environment_variables is None else serialize_environment_variables_list(data.environment_variables),
5029
- }
5030
-
5031
- ArmJointRotationsList = List[ArmJointRotations]
5032
-
5033
- def parse_arm_joint_rotations_list(data: object) -> ArmJointRotationsList:
5034
- return [parse_arm_joint_rotations(item) for item in data]
5035
-
5036
- def serialize_arm_joint_rotations_list(data: ArmJointRotationsList) -> List[object]:
5037
- return [serialize_arm_joint_rotations(item) for item in data]
5038
-
5039
5101
  @dataclass
5040
5102
  class OnRobot2FG14GripperCommandRequest:
5041
5103
  """Control the OnRobot 2FG14 gripper (end effector) of the robot
@@ -5875,7 +5937,7 @@ class Space:
5875
5937
  kind: Union[str, None] = None
5876
5938
  name: Union[str, None] = None
5877
5939
  description: Union[str, None] = None
5878
- isGlobal: Union[bool, None] = None
5940
+ is_global: Union[bool, None] = None
5879
5941
  positions: Union[SpacePositionsMap, None] = None
5880
5942
 
5881
5943
  def validate_id(self, value: str) -> Tuple[bool, str]:
@@ -5914,12 +5976,12 @@ class Space:
5914
5976
 
5915
5977
  return [True, ""]
5916
5978
 
5917
- def validate_isGlobal(self, value: bool) -> Tuple[bool, str]:
5979
+ def validate_is_global(self, value: bool) -> Tuple[bool, str]:
5918
5980
  if value is None:
5919
5981
  return [True, ""]
5920
5982
 
5921
5983
  if not isinstance(value, bool):
5922
- return [False, "isGlobal must be of type bool for Space, got " + type(value).__name__]
5984
+ return [False, "is_global must be of type bool for Space, got " + type(value).__name__]
5923
5985
 
5924
5986
  return [True, ""]
5925
5987
 
@@ -5946,7 +6008,7 @@ class Space:
5946
6008
  is_valid, error_str = self.validate_description(self.description)
5947
6009
  if not is_valid:
5948
6010
  raise TypeError(error_str)
5949
- is_valid, error_str = self.validate_isGlobal(self.isGlobal)
6011
+ is_valid, error_str = self.validate_is_global(self.is_global)
5950
6012
  if not is_valid:
5951
6013
  raise TypeError(error_str)
5952
6014
  is_valid, error_str = self.validate_positions(self.positions)
@@ -5959,7 +6021,7 @@ def parse_space(data: object):
5959
6021
  kind=parse_str(data["kind"]) if "kind" in data and data.get("kind") is not None else None,
5960
6022
  name=parse_str(data["name"]) if "name" in data and data.get("name") is not None else None,
5961
6023
  description=parse_str(data["description"]) if "description" in data and data.get("description") is not None else None,
5962
- isGlobal=parse_bool(data["isGlobal"]) if "isGlobal" in data and data.get("isGlobal") is not None else None,
6024
+ is_global=parse_bool(data["is_global"]) if "is_global" in data and data.get("is_global") is not None else None,
5963
6025
  positions=parse_space_positions_map(data["positions"]) if "positions" in data and data.get("positions") is not None else None,
5964
6026
  )
5965
6027
 
@@ -5969,7 +6031,7 @@ def serialize_space(data: Space) -> object:
5969
6031
  "kind": None if data.kind is None else serialize_str(data.kind),
5970
6032
  "name": None if data.name is None else serialize_str(data.name),
5971
6033
  "description": None if data.description is None else serialize_str(data.description),
5972
- "isGlobal": None if data.isGlobal is None else serialize_bool(data.isGlobal),
6034
+ "is_global": None if data.is_global is None else serialize_bool(data.is_global),
5973
6035
  "positions": None if data.positions is None else serialize_space_positions_map(data.positions),
5974
6036
  }
5975
6037
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: standardbots
3
- Version: 2.0.0.dev1737138088
3
+ Version: 2.0.0.dev1737492400
4
4
  Summary: Standard Bots RO1 Robotics API
5
5
  Home-page:
6
6
  Author: Standard Bots Support
@@ -1,3 +1,4 @@
1
+ README.md
1
2
  setup.py
2
3
  standardbots/__init__.py
3
4
  standardbots.egg-info/PKG-INFO
@@ -7,4 +8,8 @@ standardbots.egg-info/requires.txt
7
8
  standardbots.egg-info/top_level.txt
8
9
  standardbots/auto_generated/__init__.py
9
10
  standardbots/auto_generated/apis.py
10
- standardbots/auto_generated/models.py
11
+ standardbots/auto_generated/models.py
12
+ tests/test_apis.py
13
+ tests/fixtures/__init__.py
14
+ tests/fixtures/client_fixt.py
15
+ tests/fixtures/routines_fixt.py