viam-sdk 0.41.1__py3-none-linux_armv6l.whl → 0.66.0__py3-none-linux_armv6l.whl

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 viam-sdk might be problematic. Click here for more details.

Files changed (225) hide show
  1. viam/app/app_client.py +225 -51
  2. viam/app/billing_client.py +47 -5
  3. viam/app/data_client.py +771 -234
  4. viam/app/ml_training_client.py +3 -5
  5. viam/app/provisioning_client.py +3 -5
  6. viam/app/viam_client.py +58 -11
  7. viam/components/arm/arm.py +1 -1
  8. viam/components/arm/service.py +1 -1
  9. viam/components/audio_in/__init__.py +24 -0
  10. viam/components/audio_in/audio_in.py +74 -0
  11. viam/components/audio_in/client.py +76 -0
  12. viam/components/audio_in/service.py +83 -0
  13. viam/components/audio_out/__init__.py +21 -0
  14. viam/components/audio_out/audio_out.py +72 -0
  15. viam/components/audio_out/client.py +67 -0
  16. viam/components/audio_out/service.py +63 -0
  17. viam/components/base/base.py +1 -1
  18. viam/components/board/board.py +8 -2
  19. viam/components/board/client.py +2 -1
  20. viam/components/board/service.py +1 -0
  21. viam/components/button/__init__.py +10 -0
  22. viam/components/button/button.py +41 -0
  23. viam/components/button/client.py +52 -0
  24. viam/components/button/service.py +46 -0
  25. viam/components/camera/camera.py +15 -30
  26. viam/components/camera/client.py +10 -21
  27. viam/components/camera/service.py +15 -28
  28. viam/components/component_base.py +2 -2
  29. viam/components/gantry/client.py +17 -2
  30. viam/components/gantry/gantry.py +32 -1
  31. viam/components/gantry/service.py +21 -5
  32. viam/components/gripper/__init__.py +2 -0
  33. viam/components/gripper/client.py +25 -2
  34. viam/components/gripper/gripper.py +76 -1
  35. viam/components/gripper/service.py +32 -3
  36. viam/components/input/input.py +1 -1
  37. viam/components/motor/motor.py +1 -1
  38. viam/components/power_sensor/power_sensor.py +1 -1
  39. viam/components/switch/__init__.py +10 -0
  40. viam/components/switch/client.py +83 -0
  41. viam/components/switch/service.py +72 -0
  42. viam/components/switch/switch.py +98 -0
  43. viam/gen/app/agent/v1/agent_pb2.py +1 -1
  44. viam/gen/app/cloudslam/v1/cloud_slam_pb2.py +1 -1
  45. viam/gen/app/data/v1/data_grpc.py +74 -2
  46. viam/gen/app/data/v1/data_pb2.py +198 -104
  47. viam/gen/app/data/v1/data_pb2.pyi +563 -31
  48. viam/gen/app/datapipelines/__init__.py +0 -0
  49. viam/gen/app/datapipelines/v1/__init__.py +0 -0
  50. viam/gen/app/datapipelines/v1/data_pipelines_grpc.py +84 -0
  51. viam/gen/app/datapipelines/v1/data_pipelines_pb2.py +57 -0
  52. viam/gen/app/datapipelines/v1/data_pipelines_pb2.pyi +387 -0
  53. viam/gen/app/dataset/v1/dataset_grpc.py +10 -2
  54. viam/gen/app/dataset/v1/dataset_pb2.py +8 -4
  55. viam/gen/app/dataset/v1/dataset_pb2.pyi +36 -1
  56. viam/gen/app/datasync/v1/data_sync_pb2.py +39 -35
  57. viam/gen/app/datasync/v1/data_sync_pb2.pyi +21 -8
  58. viam/gen/app/mlinference/v1/ml_inference_pb2.py +7 -7
  59. viam/gen/app/mlinference/v1/ml_inference_pb2.pyi +4 -2
  60. viam/gen/app/mltraining/v1/ml_training_grpc.py +10 -2
  61. viam/gen/app/mltraining/v1/ml_training_pb2.py +63 -43
  62. viam/gen/app/mltraining/v1/ml_training_pb2.pyi +112 -7
  63. viam/gen/app/packages/v1/packages_pb2.py +1 -1
  64. viam/gen/app/v1/app_grpc.py +74 -3
  65. viam/gen/app/v1/app_pb2.py +600 -545
  66. viam/gen/app/v1/app_pb2.pyi +1108 -258
  67. viam/gen/app/v1/billing_grpc.py +26 -2
  68. viam/gen/app/v1/billing_pb2.py +52 -36
  69. viam/gen/app/v1/billing_pb2.pyi +158 -4
  70. viam/gen/app/v1/end_user_pb2.py +1 -1
  71. viam/gen/app/v1/robot_pb2.py +95 -89
  72. viam/gen/app/v1/robot_pb2.pyi +121 -9
  73. viam/gen/common/v1/common_pb2.py +76 -58
  74. viam/gen/common/v1/common_pb2.pyi +186 -17
  75. viam/gen/component/arm/v1/arm_grpc.py +10 -2
  76. viam/gen/component/arm/v1/arm_pb2.py +5 -3
  77. viam/gen/component/audioin/__init__.py +0 -0
  78. viam/gen/component/audioin/v1/__init__.py +0 -0
  79. viam/gen/component/audioin/v1/audioin_grpc.py +54 -0
  80. viam/gen/component/audioin/v1/audioin_pb2.py +34 -0
  81. viam/gen/component/audioin/v1/audioin_pb2.pyi +94 -0
  82. viam/gen/component/audioinput/v1/audioinput_pb2.py +1 -1
  83. viam/gen/component/audioout/__init__.py +0 -0
  84. viam/gen/component/audioout/v1/__init__.py +0 -0
  85. viam/gen/component/audioout/v1/audioout_grpc.py +54 -0
  86. viam/gen/component/audioout/v1/audioout_pb2.py +32 -0
  87. viam/gen/component/audioout/v1/audioout_pb2.pyi +47 -0
  88. viam/gen/component/base/v1/base_pb2.py +1 -1
  89. viam/gen/component/board/v1/board_pb2.py +1 -1
  90. viam/gen/component/button/v1/button_pb2.py +1 -1
  91. viam/gen/component/camera/v1/camera_grpc.py +1 -0
  92. viam/gen/component/camera/v1/camera_pb2.py +37 -36
  93. viam/gen/component/camera/v1/camera_pb2.pyi +31 -4
  94. viam/gen/component/encoder/v1/encoder_pb2.py +1 -1
  95. viam/gen/component/gantry/v1/gantry_grpc.py +9 -1
  96. viam/gen/component/gantry/v1/gantry_pb2.py +5 -3
  97. viam/gen/component/generic/v1/generic_pb2.py +1 -1
  98. viam/gen/component/gripper/v1/gripper_grpc.py +18 -2
  99. viam/gen/component/gripper/v1/gripper_pb2.py +12 -4
  100. viam/gen/component/gripper/v1/gripper_pb2.pyi +43 -1
  101. viam/gen/component/inputcontroller/v1/input_controller_pb2.py +1 -1
  102. viam/gen/component/motor/v1/motor_pb2.py +1 -1
  103. viam/gen/component/movementsensor/v1/movementsensor_pb2.py +1 -1
  104. viam/gen/component/posetracker/v1/pose_tracker_pb2.py +1 -1
  105. viam/gen/component/powersensor/v1/powersensor_pb2.py +1 -1
  106. viam/gen/component/sensor/v1/sensor_pb2.py +1 -1
  107. viam/gen/component/servo/v1/servo_pb2.py +1 -1
  108. viam/gen/component/switch/v1/switch_pb2.py +5 -5
  109. viam/gen/component/switch/v1/switch_pb2.pyi +9 -2
  110. viam/gen/component/testecho/v1/testecho_pb2.py +1 -1
  111. viam/gen/module/v1/module_pb2.py +5 -5
  112. viam/gen/module/v1/module_pb2.pyi +7 -2
  113. viam/gen/opentelemetry/__init__.py +0 -0
  114. viam/gen/opentelemetry/proto/__init__.py +0 -0
  115. viam/gen/opentelemetry/proto/common/__init__.py +0 -0
  116. viam/gen/opentelemetry/proto/common/v1/__init__.py +0 -0
  117. viam/gen/opentelemetry/proto/common/v1/common_grpc.py +0 -0
  118. viam/gen/opentelemetry/proto/common/v1/common_pb2.py +27 -0
  119. viam/gen/opentelemetry/proto/common/v1/common_pb2.pyi +208 -0
  120. viam/gen/opentelemetry/proto/resource/__init__.py +0 -0
  121. viam/gen/opentelemetry/proto/resource/v1/__init__.py +0 -0
  122. viam/gen/opentelemetry/proto/resource/v1/resource_grpc.py +0 -0
  123. viam/gen/opentelemetry/proto/resource/v1/resource_pb2.py +18 -0
  124. viam/gen/opentelemetry/proto/resource/v1/resource_pb2.pyi +59 -0
  125. viam/gen/opentelemetry/proto/trace/__init__.py +0 -0
  126. viam/gen/opentelemetry/proto/trace/v1/__init__.py +0 -0
  127. viam/gen/opentelemetry/proto/trace/v1/trace_grpc.py +0 -0
  128. viam/gen/opentelemetry/proto/trace/v1/trace_pb2.py +37 -0
  129. viam/gen/opentelemetry/proto/trace/v1/trace_pb2.pyi +402 -0
  130. viam/gen/proto/rpc/examples/echo/v1/echo_pb2.py +1 -1
  131. viam/gen/proto/rpc/examples/echoresource/v1/echoresource_pb2.py +1 -1
  132. viam/gen/proto/rpc/v1/auth_pb2.py +1 -1
  133. viam/gen/proto/rpc/webrtc/v1/grpc_pb2.py +1 -1
  134. viam/gen/proto/rpc/webrtc/v1/signaling_pb2.py +1 -1
  135. viam/gen/provisioning/v1/provisioning_grpc.py +10 -2
  136. viam/gen/provisioning/v1/provisioning_pb2.py +32 -26
  137. viam/gen/provisioning/v1/provisioning_pb2.pyi +46 -5
  138. viam/gen/robot/v1/robot_grpc.py +51 -34
  139. viam/gen/robot/v1/robot_pb2.py +147 -142
  140. viam/gen/robot/v1/robot_pb2.pyi +153 -86
  141. viam/gen/service/datamanager/v1/data_manager_grpc.py +11 -2
  142. viam/gen/service/datamanager/v1/data_manager_pb2.py +15 -8
  143. viam/gen/service/datamanager/v1/data_manager_pb2.pyi +47 -1
  144. viam/gen/service/discovery/v1/discovery_pb2.py +1 -1
  145. viam/gen/service/generic/v1/generic_pb2.py +1 -1
  146. viam/gen/service/mlmodel/v1/mlmodel_pb2.py +1 -1
  147. viam/gen/service/motion/v1/motion_pb2.py +92 -62
  148. viam/gen/service/motion/v1/motion_pb2.pyi +130 -68
  149. viam/gen/service/navigation/v1/navigation_pb2.py +1 -1
  150. viam/gen/service/sensors/v1/sensors_pb2.py +1 -1
  151. viam/gen/service/shell/v1/shell_pb2.py +1 -1
  152. viam/gen/service/slam/v1/slam_pb2.py +1 -1
  153. viam/gen/service/slam/v1/slam_pb2.pyi +1 -1
  154. viam/gen/service/video/__init__.py +0 -0
  155. viam/gen/service/video/v1/__init__.py +0 -0
  156. viam/gen/service/video/v1/video_grpc.py +39 -0
  157. viam/gen/service/video/v1/video_pb2.py +29 -0
  158. viam/gen/service/video/v1/video_pb2.pyi +72 -0
  159. viam/gen/service/vision/v1/vision_pb2.py +27 -27
  160. viam/gen/service/vision/v1/vision_pb2.pyi +28 -3
  161. viam/gen/service/worldstatestore/__init__.py +0 -0
  162. viam/gen/service/worldstatestore/v1/__init__.py +0 -0
  163. viam/gen/service/worldstatestore/v1/world_state_store_grpc.py +55 -0
  164. viam/gen/service/worldstatestore/v1/world_state_store_pb2.py +39 -0
  165. viam/gen/service/worldstatestore/v1/world_state_store_pb2.pyi +171 -0
  166. viam/gen/stream/v1/stream_pb2.py +1 -1
  167. viam/gen/tagger/v1/tagger_pb2.py +1 -1
  168. viam/logging.py +9 -8
  169. viam/media/audio.py +22 -10
  170. viam/media/utils/pil/__init__.py +5 -1
  171. viam/media/video.py +54 -40
  172. viam/module/module.py +85 -16
  173. viam/module/resource_data_consumer.py +41 -0
  174. viam/module/service.py +9 -1
  175. viam/proto/app/__init__.py +68 -0
  176. viam/proto/app/billing.py +16 -0
  177. viam/proto/app/data/__init__.py +48 -0
  178. viam/proto/app/datapipelines/__init__.py +56 -0
  179. viam/proto/app/dataset/__init__.py +4 -0
  180. viam/proto/app/mltraining/__init__.py +6 -0
  181. viam/proto/app/robot.py +6 -0
  182. viam/proto/common/__init__.py +14 -0
  183. viam/proto/component/audioin/__init__.py +16 -0
  184. viam/proto/component/audioout/__init__.py +15 -0
  185. viam/proto/component/camera/__init__.py +0 -2
  186. viam/proto/component/gripper/__init__.py +4 -0
  187. viam/proto/opentelemetry/__init__.py +0 -0
  188. viam/proto/opentelemetry/proto/__init__.py +0 -0
  189. viam/proto/opentelemetry/proto/common/__init__.py +15 -0
  190. viam/proto/opentelemetry/proto/resource/__init__.py +10 -0
  191. viam/proto/opentelemetry/proto/trace/__init__.py +15 -0
  192. viam/proto/provisioning/__init__.py +6 -0
  193. viam/proto/robot/__init__.py +16 -8
  194. viam/proto/service/datamanager/__init__.py +8 -1
  195. viam/proto/service/motion/__init__.py +2 -0
  196. viam/proto/service/video/__init__.py +15 -0
  197. viam/proto/service/worldstatestore/__init__.py +32 -0
  198. viam/resource/easy_resource.py +5 -9
  199. viam/resource/manager.py +4 -3
  200. viam/resource/registry.py +2 -2
  201. viam/resource/types.py +2 -2
  202. viam/robot/client.py +38 -59
  203. viam/rpc/dial.py +48 -5
  204. viam/rpc/libviam_rust_utils.so +0 -0
  205. viam/rpc/server.py +24 -10
  206. viam/services/motion/client.py +8 -9
  207. viam/services/motion/motion.py +48 -46
  208. viam/services/navigation/navigation.py +2 -2
  209. viam/services/vision/client.py +1 -1
  210. viam/services/vision/service.py +5 -8
  211. viam/services/vision/vision.py +5 -3
  212. viam/services/worldstatestore/__init__.py +18 -0
  213. viam/services/worldstatestore/client.py +94 -0
  214. viam/services/worldstatestore/service.py +55 -0
  215. viam/services/worldstatestore/worldstatestore.py +90 -0
  216. viam/sessions_client.py +115 -46
  217. viam/version_metadata.py +2 -2
  218. {viam_sdk-0.41.1.dist-info → viam_sdk-0.66.0.dist-info}/METADATA +10 -6
  219. {viam_sdk-0.41.1.dist-info → viam_sdk-0.66.0.dist-info}/RECORD +221 -152
  220. {viam_sdk-0.41.1.dist-info → viam_sdk-0.66.0.dist-info}/WHEEL +1 -1
  221. viam/components/audio_input/__init__.py +0 -18
  222. viam/components/audio_input/audio_input.py +0 -81
  223. viam/components/audio_input/client.py +0 -70
  224. viam/components/audio_input/service.py +0 -114
  225. {viam_sdk-0.41.1.dist-info → viam_sdk-0.66.0.dist-info}/licenses/LICENSE +0 -0
@@ -46,11 +46,9 @@ class MLTrainingClient:
46
46
  async def main():
47
47
 
48
48
  # Make a ViamClient
49
- viam_client = await connect()
50
- # Instantiate an MLTrainingClient to run ML training client API methods on
51
- ml_training_client = viam_client.ml_training_client
52
-
53
- viam_client.close()
49
+ async with await connect() as viam_client:
50
+ # Instantiate an MLTrainingClient to run ML training client API methods on
51
+ ml_training_client = viam_client.ml_training_client
54
52
 
55
53
  if __name__ == '__main__':
56
54
  asyncio.run(main())
@@ -41,11 +41,9 @@ class ProvisioningClient:
41
41
  async def main():
42
42
 
43
43
  # Make a ViamClient
44
- viam_client = await connect()
45
- # Instantiate a ProvisioningClient to run provisioning client API methods on
46
- provisioning_client = viam_client.provisioning_client
47
-
48
- viam_client.close()
44
+ async with await connect() as viam_client:
45
+ # Instantiate a ProvisioningClient to run provisioning client API methods on
46
+ provisioning_client = viam_client.provisioning_client
49
47
 
50
48
  if __name__ == '__main__':
51
49
  asyncio.run(main())
viam/app/viam_client.py CHANGED
@@ -1,4 +1,5 @@
1
- from typing import Mapping, Optional
1
+ import os
2
+ from typing import Any, Mapping, Optional
2
3
 
3
4
  from grpclib.client import Channel
4
5
  from typing_extensions import Self
@@ -10,7 +11,7 @@ from viam.app.data_client import DataClient
10
11
  from viam.app.ml_training_client import MLTrainingClient
11
12
  from viam.app.provisioning_client import ProvisioningClient
12
13
  from viam.robot.client import RobotClient
13
- from viam.rpc.dial import DialOptions, _dial_app, _get_access_token
14
+ from viam.rpc.dial import Credentials, DialOptions, _dial_app, _get_access_token
14
15
 
15
16
  LOGGER = logging.getLogger(__name__)
16
17
 
@@ -19,19 +20,51 @@ class ViamClient:
19
20
  """gRPC client for all communication and interaction with app.
20
21
 
21
22
  `ViamClient` class for creating and managing specialized client instances.
22
- There is currently 1 way to instantiate a `ViamClient` object::
23
+ There are currently 2 ways to instantiate a `ViamClient` object::
23
24
 
24
25
  ViamClient.create_from_dial_options(...)
26
+ ViamClient.create_from_env_vars()
25
27
  """
26
28
 
29
+ @classmethod
30
+ async def create_from_env_vars(cls, dial_options: Optional[DialOptions] = None, app_url: Optional[str] = None) -> Self:
31
+ """Create `ViamClient` using credentials set in the environment as `VIAM_API_KEY` and `VIAM_API_KEY_ID`.
32
+
33
+ ::
34
+
35
+ client = await ViamClient.create_from_env_vars()
36
+
37
+ Args:
38
+ dial_options (Optional[viam.rpc.dial.DialOptions]): Options for authorization and connection to app.
39
+ If not provided, default options will be selected. Note that `creds` and `auth_entity`
40
+ fields will be overwritten by the values set by a module.
41
+ app_url: (Optional[str]): URL of app. Uses app.viam.com if not specified.
42
+
43
+ Raises:
44
+ ValueError: If there are no env vars set by the module, or if they are set improperly
45
+
46
+ """
47
+ dial_options = dial_options if dial_options else DialOptions()
48
+ api_key = os.environ.get("VIAM_API_KEY")
49
+ if api_key is None:
50
+ raise ValueError("api key cannot be None")
51
+ api_key_id = os.environ.get("VIAM_API_KEY_ID")
52
+ if api_key_id is None:
53
+ raise ValueError("api key ID cannot be None")
54
+ credentials = Credentials(type="api-key", payload=api_key)
55
+ dial_options.credentials = credentials
56
+ dial_options.auth_entity = api_key_id
57
+
58
+ return await cls.create_from_dial_options(dial_options, app_url)
59
+
27
60
  @classmethod
28
61
  async def create_from_dial_options(cls, dial_options: DialOptions, app_url: Optional[str] = None) -> Self:
29
- """Create `ViamClient` that establishes a connection to the Viam app.
62
+ """Create `ViamClient` that establishes a connection to Viam.
30
63
 
31
64
  ::
32
65
 
33
66
  dial_options = DialOptions.with_api_key("<API-KEY>", "<API-KEY-ID>")
34
- ViamClient.create_from_dial_options(dial_options)
67
+ client = await ViamClient.create_from_dial_options(dial_options)
35
68
 
36
69
  Args:
37
70
  dial_options (viam.rpc.dial.DialOptions): Required information for authorization and connection to app.
@@ -53,9 +86,6 @@ class ViamClient:
53
86
 
54
87
  self = cls()
55
88
  self._dial_options = dial_options
56
- self._location_id = None
57
- if dial_options.credentials.type == "robot-location-secret":
58
- self._location_id = dial_options.auth_entity.split(".")[1]
59
89
  if app_url is None:
60
90
  app_url = "app.viam.com"
61
91
  self._channel = await _dial_app(app_url)
@@ -66,7 +96,6 @@ class ViamClient:
66
96
  _channel: Channel
67
97
  _metadata: Mapping[str, str]
68
98
  _closed: bool = False
69
- _location_id: Optional[str]
70
99
  _dial_options: DialOptions
71
100
 
72
101
  @property
@@ -108,7 +137,7 @@ class ViamClient:
108
137
  # Instantiate an AppClient called "fleet" to run fleet management API methods on
109
138
  fleet = viam_client.app_client
110
139
  """
111
- return AppClient(self._channel, self._metadata, self._location_id)
140
+ return AppClient(self._channel, self._metadata)
112
141
 
113
142
  @property
114
143
  def ml_training_client(self) -> MLTrainingClient:
@@ -174,7 +203,25 @@ class ViamClient:
174
203
  """
175
204
  return ProvisioningClient(self._channel, self._metadata)
176
205
 
177
- def close(self):
206
+ async def __aenter__(self) -> "ViamClient":
207
+ """A ViamClient can act as an asynchronous context manager. It will do nothing special when
208
+ the context is entered.
209
+ """
210
+ return self
211
+
212
+ async def __aexit__(self, exc_type: Optional[type], exc_value: Optional[BaseException], traceback: Optional[Any]) -> None:
213
+ """A ViamClient can act as an asynchronous context manager. It will close itself when
214
+ the context is exited.
215
+
216
+ ::
217
+
218
+ async with ViamClient.create_from_dial_options(...) as client:
219
+ await do_something_with(client)
220
+ # client is closed here
221
+ """
222
+ self.close()
223
+
224
+ def close(self) -> None:
178
225
  """Close opened channels used for the various service stubs initialized."""
179
226
  if self._closed:
180
227
  LOGGER.debug("ViamClient is already closed.")
@@ -194,7 +194,7 @@ class Arm(ComponentBase):
194
194
 
195
195
  @abc.abstractmethod
196
196
  async def get_kinematics(
197
- self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None
197
+ self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs
198
198
  ) -> Tuple[KinematicsFileFormat.ValueType, bytes]:
199
199
  """
200
200
  Get the kinematics information associated with the arm.
@@ -109,7 +109,7 @@ class ArmRPCService(UnimplementedArmServiceBase, ResourceRPCServiceBase[Arm]):
109
109
  assert request is not None
110
110
  arm = self.get_resource(request.name)
111
111
  timeout = stream.deadline.time_remaining() if stream.deadline else None
112
- format, kinematics_data = await arm.get_kinematics(extra=struct_to_dict(request.extra), timeout=timeout)
112
+ format, kinematics_data = await arm.get_kinematics(extra=struct_to_dict(request.extra), timeout=timeout, metadata=stream.metadata)
113
113
  response = GetKinematicsResponse(format=format, kinematics_data=kinematics_data)
114
114
  await stream.send_message(response)
115
115
 
@@ -0,0 +1,24 @@
1
+ from viam.media.audio import AudioCodec
2
+ from viam.proto.common import AudioInfo
3
+ from viam.resource.registry import Registry, ResourceRegistration
4
+
5
+ from .audio_in import AudioIn
6
+ from .client import AudioInClient
7
+ from .service import AudioInRPCService
8
+
9
+ AudioResponse = AudioIn.AudioResponse
10
+
11
+ __all__ = [
12
+ "AudioIn",
13
+ "AudioResponse",
14
+ "AudioInfo",
15
+ "AudioCodec",
16
+ ]
17
+
18
+ Registry.register_api(
19
+ ResourceRegistration(
20
+ AudioIn,
21
+ AudioInRPCService,
22
+ lambda name, channel: AudioInClient(name, channel),
23
+ )
24
+ )
@@ -0,0 +1,74 @@
1
+ import abc
2
+ import sys
3
+ from typing import Final, Optional
4
+
5
+ from viam.proto.common import GetPropertiesResponse
6
+ from viam.proto.component.audioin import GetAudioResponse
7
+ from viam.resource.types import API, RESOURCE_NAMESPACE_RDK, RESOURCE_TYPE_COMPONENT
8
+ from viam.streams import Stream
9
+
10
+ from ..component_base import ComponentBase
11
+
12
+ if sys.version_info >= (3, 10):
13
+ from typing import TypeAlias
14
+ else:
15
+ from typing_extensions import TypeAlias
16
+
17
+
18
+ class AudioIn(ComponentBase):
19
+ """AudioIn represents a component that can capture audio.
20
+
21
+ This acts as an abstract base class for any drivers representing specific
22
+ audio input implementations. This cannot be used on its own. If the ``__init__()`` function is
23
+ overridden, it must call the ``super().__init__()`` function.
24
+ """
25
+
26
+ API: Final = API( # pyright: ignore [reportIncompatibleVariableOverride]
27
+ RESOURCE_NAMESPACE_RDK, RESOURCE_TYPE_COMPONENT, "audio_in"
28
+ )
29
+
30
+ Properties: "TypeAlias" = GetPropertiesResponse
31
+ AudioResponse: "TypeAlias" = GetAudioResponse
32
+ AudioStream = Stream[AudioResponse]
33
+
34
+ @abc.abstractmethod
35
+ async def get_audio(
36
+ self, codec: str, duration_seconds: float, previous_timestamp_ns: int, *, timeout: Optional[float] = None, **kwargs
37
+ ) -> AudioStream:
38
+ """
39
+ Get a stream of audio from the device
40
+
41
+ ::
42
+
43
+ my_audio_in = AudioIn.from_robot(robot=machine, name="my_audio_in")
44
+
45
+ stream = await my_audio_in.get_audio(
46
+ codec=AudioCodec.PCM16,
47
+ duration_seconds=10.0,
48
+ previous_timestamp_ns=0
49
+ )
50
+
51
+ Args:
52
+ codec (str): The desired codec of the returned audio data
53
+ duration_seconds (float): duration of the stream. 0 = indefinite stream
54
+ previous_timestamp_ns (int): starting timestamp in nanoseconds for recording continuity.
55
+ Set to 0 to begin recording from the current time.
56
+ Returns:
57
+ AudioStream: stream of audio chunks.
58
+ ...
59
+ """
60
+
61
+ @abc.abstractmethod
62
+ async def get_properties(self, *, timeout: Optional[float] = None, **kwargs) -> Properties:
63
+ """
64
+ Get the audio device's properties
65
+
66
+ ::
67
+
68
+ my_audio_in = AudioIn.from_robot(robot=machine, name="my_audio_in")
69
+ properties = await my_audio_in.get_properties()
70
+
71
+ Returns:
72
+ Properties: The properties of the audio in device.
73
+ ...
74
+ """
@@ -0,0 +1,76 @@
1
+ import uuid
2
+ from typing import Any, Dict, List, Mapping, Optional
3
+
4
+ from grpclib.client import Channel
5
+ from grpclib.client import Stream as ClientStream
6
+
7
+ from viam.proto.common import DoCommandRequest, DoCommandResponse, Geometry, GetPropertiesRequest
8
+ from viam.proto.component.audioin import AudioInServiceStub, GetAudioRequest, GetAudioResponse
9
+ from viam.resource.rpc_client_base import ReconfigurableResourceRPCClientBase
10
+ from viam.streams import StreamWithIterator
11
+ from viam.utils import ValueTypes, dict_to_struct, get_geometries, struct_to_dict
12
+
13
+ from .audio_in import AudioIn
14
+
15
+
16
+ class AudioInClient(AudioIn, ReconfigurableResourceRPCClientBase):
17
+ def __init__(self, name: str, channel: Channel) -> None:
18
+ self.channel = channel
19
+ self.client = AudioInServiceStub(channel)
20
+ super().__init__(name)
21
+
22
+ async def get_audio(
23
+ self,
24
+ codec: str,
25
+ duration_seconds: float,
26
+ previous_timestamp_ns: int,
27
+ *,
28
+ extra: Optional[Dict[str, Any]] = None,
29
+ **kwargs,
30
+ ):
31
+ request = GetAudioRequest(
32
+ name=self.name,
33
+ codec=codec,
34
+ duration_seconds=duration_seconds,
35
+ previous_timestamp_nanoseconds=previous_timestamp_ns,
36
+ request_id=str(uuid.uuid4()),
37
+ extra=dict_to_struct(extra),
38
+ )
39
+
40
+ async def read():
41
+ md = kwargs.get("metadata", self.Metadata()).proto
42
+ audio_stream: ClientStream[GetAudioRequest, GetAudioResponse]
43
+ async with self.client.GetAudio.open(metadata=md) as audio_stream:
44
+ try:
45
+ await audio_stream.send_message(request, end=True)
46
+ async for response in audio_stream:
47
+ yield response
48
+ except Exception as e:
49
+ raise (e)
50
+
51
+ return StreamWithIterator(read())
52
+
53
+ async def get_properties(
54
+ self,
55
+ *,
56
+ timeout: Optional[float] = None,
57
+ **kwargs,
58
+ ) -> AudioIn.Properties:
59
+ md = kwargs.get("metadata", self.Metadata()).proto
60
+ return await self.client.GetProperties(GetPropertiesRequest(name=self.name), timeout=timeout, metadata=md)
61
+
62
+ async def do_command(
63
+ self,
64
+ command: Mapping[str, ValueTypes],
65
+ *,
66
+ timeout: Optional[float] = None,
67
+ **kwargs,
68
+ ) -> Mapping[str, ValueTypes]:
69
+ md = kwargs.get("metadata", self.Metadata()).proto
70
+ request = DoCommandRequest(name=self.name, command=dict_to_struct(command))
71
+ response: DoCommandResponse = await self.client.DoCommand(request, timeout=timeout, metadata=md)
72
+ return struct_to_dict(response.result)
73
+
74
+ async def get_geometries(self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs) -> List[Geometry]:
75
+ md = kwargs.get("metadata", self.Metadata())
76
+ return await get_geometries(self.client, self.name, extra, timeout, md)
@@ -0,0 +1,83 @@
1
+ from grpclib.server import Stream
2
+ from h2.exceptions import StreamClosedError
3
+
4
+ from viam.logging import getLogger
5
+ from viam.proto.common import (
6
+ DoCommandRequest,
7
+ DoCommandResponse,
8
+ GetGeometriesRequest,
9
+ GetGeometriesResponse,
10
+ GetPropertiesRequest,
11
+ GetPropertiesResponse,
12
+ )
13
+ from viam.proto.component.audioin import AudioInServiceBase, GetAudioRequest, GetAudioResponse
14
+ from viam.resource.rpc_service_base import ResourceRPCServiceBase
15
+ from viam.utils import dict_to_struct, struct_to_dict
16
+
17
+ from .audio_in import AudioIn
18
+
19
+ LOGGER = getLogger(__name__)
20
+
21
+
22
+ class AudioInRPCService(AudioInServiceBase, ResourceRPCServiceBase[AudioIn]):
23
+ """
24
+ gRPC Service for a generic audio in.
25
+ """
26
+
27
+ RESOURCE_TYPE = AudioIn
28
+
29
+ async def GetAudio(self, stream: Stream[GetAudioRequest, GetAudioResponse]) -> None:
30
+ request = await stream.recv_message()
31
+ assert request is not None
32
+ name = request.name
33
+ audio_in = self.get_resource(name)
34
+ audio_stream = await audio_in.get_audio(
35
+ codec=request.codec,
36
+ duration_seconds=request.duration_seconds,
37
+ previous_timestamp_ns=request.previous_timestamp_nanoseconds,
38
+ metadata=stream.metadata,
39
+ )
40
+ async for response in audio_stream:
41
+ try:
42
+ response.request_id = request.request_id
43
+ await stream.send_message(response)
44
+ except StreamClosedError:
45
+ return
46
+ except Exception as e:
47
+ LOGGER.error(e)
48
+ return
49
+
50
+ async def GetProperties(self, stream: Stream[GetPropertiesRequest, GetPropertiesResponse]) -> None:
51
+ request = await stream.recv_message()
52
+ assert request is not None
53
+ name = request.name
54
+ audio_in = self.get_resource(name)
55
+ timeout = stream.deadline.time_remaining() if stream.deadline else None
56
+ properties = await audio_in.get_properties(
57
+ timeout=timeout,
58
+ metadata=stream.metadata,
59
+ )
60
+ await stream.send_message(properties)
61
+
62
+ async def DoCommand(self, stream: Stream[DoCommandRequest, DoCommandResponse]) -> None:
63
+ request = await stream.recv_message()
64
+ assert request is not None
65
+ name = request.name
66
+ audio_in = self.get_resource(name)
67
+ timeout = stream.deadline.time_remaining() if stream.deadline else None
68
+ result = await audio_in.do_command(
69
+ command=struct_to_dict(request.command),
70
+ timeout=timeout,
71
+ metadata=stream.metadata,
72
+ )
73
+ response = DoCommandResponse(result=dict_to_struct(result))
74
+ await stream.send_message(response)
75
+
76
+ async def GetGeometries(self, stream: Stream[GetGeometriesRequest, GetGeometriesResponse]) -> None:
77
+ request = await stream.recv_message()
78
+ assert request is not None
79
+ arm = self.get_resource(request.name)
80
+ timeout = stream.deadline.time_remaining() if stream.deadline else None
81
+ geometries = await arm.get_geometries(extra=struct_to_dict(request.extra), timeout=timeout)
82
+ response = GetGeometriesResponse(geometries=geometries)
83
+ await stream.send_message(response)
@@ -0,0 +1,21 @@
1
+ from viam.media.audio import AudioCodec
2
+ from viam.proto.common import AudioInfo
3
+ from viam.resource.registry import Registry, ResourceRegistration
4
+
5
+ from .audio_out import AudioOut
6
+ from .client import AudioOutClient
7
+ from .service import AudioOutRPCService
8
+
9
+ __all__ = [
10
+ "AudioOut",
11
+ "AudioInfo",
12
+ "AudioCodec",
13
+ ]
14
+
15
+ Registry.register_api(
16
+ ResourceRegistration(
17
+ AudioOut,
18
+ AudioOutRPCService,
19
+ lambda name, channel: AudioOutClient(name, channel),
20
+ )
21
+ )
@@ -0,0 +1,72 @@
1
+ import abc
2
+ import sys
3
+ from typing import Any, Dict, Final, Optional
4
+
5
+ from viam.proto.common import GetPropertiesResponse
6
+ from viam.resource.types import API, RESOURCE_NAMESPACE_RDK, RESOURCE_TYPE_COMPONENT
7
+
8
+ from ..component_base import ComponentBase
9
+ from . import AudioInfo
10
+
11
+ if sys.version_info >= (3, 10):
12
+ from typing import TypeAlias
13
+ else:
14
+ from typing_extensions import TypeAlias
15
+
16
+
17
+ class AudioOut(ComponentBase):
18
+ """AudioOut represents a component that can play audio.
19
+
20
+ This acts as an abstract base class for any drivers representing specific
21
+ audio output implementations. This cannot be used on its own. If the ``__init__()`` function is
22
+ overridden, it must call the ``super().__init__()`` function.
23
+ """
24
+
25
+ API: Final = API( # pyright: ignore [reportIncompatibleVariableOverride]
26
+ RESOURCE_NAMESPACE_RDK, RESOURCE_TYPE_COMPONENT, "audio_out"
27
+ )
28
+
29
+ Properties: "TypeAlias" = GetPropertiesResponse
30
+
31
+ @abc.abstractmethod
32
+ async def play(
33
+ self,
34
+ data: bytes,
35
+ info: Optional[AudioInfo] = None,
36
+ *,
37
+ extra: Optional[Dict[str, Any]] = None,
38
+ timeout: Optional[float] = None,
39
+ **kwargs,
40
+ ) -> None:
41
+ """
42
+ Play the given audio data.
43
+
44
+ ::
45
+
46
+ my_audio_out = AudioOut.from_robot(robot=machine, name="my_audio_out")
47
+
48
+ # With audio info
49
+ audio_info = AudioInfo(codec=AudioCodec.PCM16, sample_rate_hz=44100, num_channels=2)
50
+ await my_audio_out.play(audio_data, audio_info)
51
+
52
+ # Without audio info (when codec encodes information within audio_data)
53
+ await my_audio_out.play(audio_data)
54
+
55
+ Args:
56
+ data: audio bytes to play
57
+ info: (optional) information about the audio data such as codec, sample rate, and channel count
58
+ """
59
+
60
+ @abc.abstractmethod
61
+ async def get_properties(self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs) -> Properties:
62
+ """
63
+ Get the audio output device's properties.
64
+
65
+ ::
66
+
67
+ my_audio_out = AudioOut.from_robot(robot=machine, name="my_audio_out")
68
+ properties = await my_audio_out.get_properties()
69
+
70
+ Returns:
71
+ Properties: The properties of the audio output device
72
+ """
@@ -0,0 +1,67 @@
1
+ from typing import Any, Dict, List, Mapping, Optional
2
+
3
+ from grpclib.client import Channel
4
+
5
+ from viam.proto.common import DoCommandRequest, DoCommandResponse, Geometry, GetPropertiesRequest, GetPropertiesResponse
6
+ from viam.proto.component.audioout import AudioOutServiceStub, PlayRequest
7
+ from viam.resource.rpc_client_base import ReconfigurableResourceRPCClientBase
8
+ from viam.utils import ValueTypes, dict_to_struct, get_geometries, struct_to_dict
9
+
10
+ from .audio_out import AudioInfo, AudioOut
11
+
12
+
13
+ class AudioOutClient(AudioOut, ReconfigurableResourceRPCClientBase):
14
+ """gRPC client for AudioOut component."""
15
+
16
+ def __init__(self, name: str, channel: Channel) -> None:
17
+ self.channel = channel
18
+ self.client = AudioOutServiceStub(channel)
19
+ super().__init__(name)
20
+
21
+ async def play(
22
+ self,
23
+ data: bytes,
24
+ info: Optional[AudioInfo] = None,
25
+ *,
26
+ extra: Optional[Dict[str, Any]] = None,
27
+ timeout: Optional[float] = None,
28
+ **kwargs,
29
+ ) -> None:
30
+ if extra is None:
31
+ extra = {}
32
+
33
+ md = kwargs.get("metadata", self.Metadata()).proto
34
+ request = PlayRequest(
35
+ name=self.name,
36
+ audio_data=data,
37
+ audio_info=info,
38
+ extra=dict_to_struct(extra),
39
+ )
40
+ await self.client.Play(request, timeout=timeout, metadata=md)
41
+
42
+ async def get_properties(
43
+ self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs
44
+ ) -> AudioOut.Properties:
45
+ if extra is None:
46
+ extra = {}
47
+
48
+ md = kwargs.get("metadata", self.Metadata()).proto
49
+ request = GetPropertiesRequest(name=self.name, extra=dict_to_struct(extra))
50
+ response: GetPropertiesResponse = await self.client.GetProperties(request, timeout=timeout, metadata=md)
51
+ return response
52
+
53
+ async def do_command(
54
+ self,
55
+ command: Mapping[str, ValueTypes],
56
+ *,
57
+ timeout: Optional[float] = None,
58
+ **kwargs,
59
+ ) -> Mapping[str, ValueTypes]:
60
+ md = kwargs.get("metadata", self.Metadata()).proto
61
+ request = DoCommandRequest(name=self.name, command=dict_to_struct(command))
62
+ response: DoCommandResponse = await self.client.DoCommand(request, timeout=timeout, metadata=md)
63
+ return struct_to_dict(response.result)
64
+
65
+ async def get_geometries(self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs) -> List[Geometry]:
66
+ md = kwargs.get("metadata", self.Metadata())
67
+ return await get_geometries(self.client, self.name, extra, timeout, md)
@@ -0,0 +1,63 @@
1
+ from grpclib.server import Stream
2
+
3
+ from viam.proto.common import (
4
+ DoCommandRequest,
5
+ DoCommandResponse,
6
+ GetGeometriesRequest,
7
+ GetGeometriesResponse,
8
+ GetPropertiesRequest,
9
+ GetPropertiesResponse,
10
+ )
11
+ from viam.proto.component.audioout import (
12
+ AudioOutServiceBase,
13
+ PlayRequest,
14
+ PlayResponse,
15
+ )
16
+ from viam.resource.rpc_service_base import ResourceRPCServiceBase
17
+ from viam.utils import dict_to_struct, struct_to_dict
18
+
19
+ from .audio_out import AudioOut
20
+
21
+
22
+ class AudioOutRPCService(AudioOutServiceBase, ResourceRPCServiceBase[AudioOut]):
23
+ """gRPC service for AudioOut component."""
24
+
25
+ RESOURCE_TYPE = AudioOut
26
+
27
+ async def Play(self, stream: Stream[PlayRequest, PlayResponse]) -> None:
28
+ request = await stream.recv_message()
29
+ assert request is not None
30
+ name = request.name
31
+ audio_out = self.get_resource(name)
32
+ # Check if audio_info was provided in the request
33
+ audio_info = request.audio_info if request.HasField("audio_info") else None
34
+ timeout = stream.deadline.time_remaining() if stream.deadline else None
35
+ await audio_out.play(request.audio_data, audio_info, extra=struct_to_dict(request.extra), timeout=timeout, metadata=stream.metadata)
36
+ await stream.send_message(PlayResponse())
37
+
38
+ async def GetProperties(self, stream: Stream[GetPropertiesRequest, GetPropertiesResponse]) -> None:
39
+ request = await stream.recv_message()
40
+ assert request is not None
41
+ name = request.name
42
+ audio_out = self.get_resource(name)
43
+ timeout = stream.deadline.time_remaining() if stream.deadline else None
44
+ properties = await audio_out.get_properties(extra=struct_to_dict(request.extra), timeout=timeout, metadata=stream.metadata)
45
+ await stream.send_message(properties)
46
+
47
+ async def DoCommand(self, stream: Stream[DoCommandRequest, DoCommandResponse]) -> None:
48
+ request = await stream.recv_message()
49
+ assert request is not None
50
+ audio_out = self.get_resource(request.name)
51
+ timeout = stream.deadline.time_remaining() if stream.deadline else None
52
+ result = await audio_out.do_command(command=struct_to_dict(request.command), timeout=timeout, metadata=stream.metadata)
53
+ response = DoCommandResponse(result=dict_to_struct(result))
54
+ await stream.send_message(response)
55
+
56
+ async def GetGeometries(self, stream: Stream[GetGeometriesRequest, GetGeometriesResponse]) -> None:
57
+ request = await stream.recv_message()
58
+ assert request is not None
59
+ audio_out = self.get_resource(request.name)
60
+ timeout = stream.deadline.time_remaining() if stream.deadline else None
61
+ geometries = await audio_out.get_geometries(extra=struct_to_dict(request.extra), timeout=timeout)
62
+ response = GetGeometriesResponse(geometries=geometries)
63
+ await stream.send_message(response)
@@ -126,7 +126,7 @@ class Base(ComponentBase):
126
126
  # Make your wheeled base move forward. Set linear power to 75%.
127
127
  print("move forward")
128
128
  await my_base.set_power(
129
- linear=Vector3(x=0, y=-.75, z=0),
129
+ linear=Vector3(x=0, y=.75, z=0),
130
130
  angular=Vector3(x=0, y=0, z=0))
131
131
 
132
132
  # Make your wheeled base move backward. Set linear power to -100%.