viam-sdk 0.3.0__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 (452) hide show
  1. viam/__init__.py +29 -2
  2. viam/app/_logs.py +34 -0
  3. viam/app/app_client.py +2696 -0
  4. viam/app/billing_client.py +185 -0
  5. viam/app/data_client.py +2231 -0
  6. viam/app/ml_training_client.py +249 -0
  7. viam/app/provisioning_client.py +93 -0
  8. viam/app/viam_client.py +275 -0
  9. viam/components/arm/__init__.py +3 -26
  10. viam/components/arm/arm.py +123 -8
  11. viam/components/arm/client.py +37 -24
  12. viam/components/arm/service.py +35 -32
  13. viam/components/audio_in/__init__.py +24 -0
  14. viam/components/audio_in/audio_in.py +74 -0
  15. viam/components/audio_in/client.py +76 -0
  16. viam/components/audio_in/service.py +83 -0
  17. viam/components/audio_out/__init__.py +21 -0
  18. viam/components/audio_out/audio_out.py +72 -0
  19. viam/components/audio_out/client.py +67 -0
  20. viam/components/audio_out/service.py +63 -0
  21. viam/components/base/__init__.py +6 -11
  22. viam/components/base/base.py +134 -8
  23. viam/components/base/client.py +51 -23
  24. viam/components/base/service.py +33 -30
  25. viam/components/board/__init__.py +3 -12
  26. viam/components/board/board.py +247 -91
  27. viam/components/board/client.py +149 -83
  28. viam/components/board/service.py +63 -33
  29. viam/components/button/__init__.py +10 -0
  30. viam/components/button/button.py +41 -0
  31. viam/components/button/client.py +52 -0
  32. viam/components/button/service.py +46 -0
  33. viam/components/camera/__init__.py +3 -3
  34. viam/components/camera/camera.py +62 -27
  35. viam/components/camera/client.py +59 -27
  36. viam/components/camera/service.py +42 -65
  37. viam/components/component_base.py +28 -5
  38. viam/components/encoder/__init__.py +1 -1
  39. viam/components/encoder/client.py +25 -14
  40. viam/components/encoder/encoder.py +48 -10
  41. viam/components/encoder/service.py +14 -18
  42. viam/components/gantry/__init__.py +1 -13
  43. viam/components/gantry/client.py +80 -25
  44. viam/components/gantry/gantry.py +123 -9
  45. viam/components/gantry/service.py +51 -29
  46. viam/components/generic/__init__.py +1 -1
  47. viam/components/generic/client.py +21 -8
  48. viam/components/generic/generic.py +10 -2
  49. viam/components/generic/service.py +12 -7
  50. viam/components/gripper/__init__.py +3 -13
  51. viam/components/gripper/client.py +69 -21
  52. viam/components/gripper/gripper.py +123 -3
  53. viam/components/gripper/service.py +44 -22
  54. viam/components/input/__init__.py +1 -14
  55. viam/components/input/client.py +55 -23
  56. viam/components/input/input.py +106 -3
  57. viam/components/input/service.py +16 -21
  58. viam/components/motor/__init__.py +1 -21
  59. viam/components/motor/client.py +56 -33
  60. viam/components/motor/motor.py +127 -4
  61. viam/components/motor/service.py +33 -44
  62. viam/components/movement_sensor/__init__.py +1 -1
  63. viam/components/movement_sensor/client.py +102 -45
  64. viam/components/movement_sensor/movement_sensor.py +130 -61
  65. viam/components/movement_sensor/service.py +38 -41
  66. viam/components/pose_tracker/__init__.py +1 -1
  67. viam/components/pose_tracker/client.py +18 -7
  68. viam/components/pose_tracker/pose_tracker.py +4 -2
  69. viam/components/pose_tracker/service.py +12 -10
  70. viam/components/power_sensor/__init__.py +17 -0
  71. viam/components/power_sensor/client.py +86 -0
  72. viam/components/power_sensor/power_sensor.py +104 -0
  73. viam/components/power_sensor/service.py +72 -0
  74. viam/components/sensor/__init__.py +2 -1
  75. viam/components/sensor/client.py +26 -10
  76. viam/components/sensor/sensor.py +22 -4
  77. viam/components/sensor/service.py +20 -11
  78. viam/components/servo/__init__.py +1 -13
  79. viam/components/servo/client.py +47 -21
  80. viam/components/servo/service.py +15 -22
  81. viam/components/servo/servo.py +61 -2
  82. viam/components/switch/__init__.py +10 -0
  83. viam/components/switch/client.py +83 -0
  84. viam/components/switch/service.py +72 -0
  85. viam/components/switch/switch.py +98 -0
  86. viam/errors.py +10 -0
  87. viam/gen/app/agent/v1/agent_grpc.py +29 -0
  88. viam/gen/app/agent/v1/agent_pb2.py +47 -0
  89. viam/gen/app/agent/v1/agent_pb2.pyi +280 -0
  90. viam/gen/app/cloudslam/v1/__init__.py +0 -0
  91. viam/gen/app/cloudslam/v1/cloud_slam_grpc.py +70 -0
  92. viam/gen/app/cloudslam/v1/cloud_slam_pb2.py +54 -0
  93. viam/gen/app/cloudslam/v1/cloud_slam_pb2.pyi +384 -0
  94. viam/gen/app/data/v1/data_grpc.py +197 -8
  95. viam/gen/app/data/v1/data_pb2.py +238 -99
  96. viam/gen/app/data/v1/data_pb2.pyi +1222 -259
  97. viam/gen/app/datapipelines/__init__.py +0 -0
  98. viam/gen/app/datapipelines/v1/__init__.py +0 -0
  99. viam/gen/app/datapipelines/v1/data_pipelines_grpc.py +84 -0
  100. viam/gen/app/datapipelines/v1/data_pipelines_pb2.py +57 -0
  101. viam/gen/app/datapipelines/v1/data_pipelines_pb2.pyi +387 -0
  102. viam/gen/app/dataset/__init__.py +0 -0
  103. viam/gen/app/dataset/v1/__init__.py +0 -0
  104. viam/gen/app/dataset/v1/dataset_grpc.py +68 -0
  105. viam/gen/app/dataset/v1/dataset_pb2.py +44 -0
  106. viam/gen/app/dataset/v1/dataset_pb2.pyi +214 -0
  107. viam/gen/app/datasync/v1/data_sync_grpc.py +21 -4
  108. viam/gen/app/datasync/v1/data_sync_pb2.py +62 -128
  109. viam/gen/app/datasync/v1/data_sync_pb2.pyi +156 -199
  110. viam/gen/app/mlinference/__init__.py +0 -0
  111. viam/gen/app/mlinference/v1/__init__.py +0 -0
  112. viam/gen/app/mlinference/v1/ml_inference_grpc.py +28 -0
  113. viam/gen/app/mlinference/v1/ml_inference_pb2.py +23 -0
  114. viam/gen/app/mlinference/v1/ml_inference_pb2.pyi +63 -0
  115. viam/gen/app/mltraining/v1/ml_training_grpc.py +51 -3
  116. viam/gen/app/mltraining/v1/ml_training_pb2.py +135 -58
  117. viam/gen/app/mltraining/v1/ml_training_pb2.pyi +328 -39
  118. viam/gen/app/packages/v1/packages_grpc.py +15 -1
  119. viam/gen/app/packages/v1/packages_pb2.py +44 -64
  120. viam/gen/app/packages/v1/packages_pb2.pyi +75 -85
  121. viam/gen/app/v1/app_grpc.py +644 -3
  122. viam/gen/app/v1/app_pb2.py +695 -295
  123. viam/gen/app/v1/app_pb2.pyi +4488 -635
  124. viam/gen/app/v1/billing_grpc.py +53 -11
  125. viam/gen/app/v1/billing_pb2.py +94 -39
  126. viam/gen/app/v1/billing_pb2.pyi +391 -191
  127. viam/gen/app/v1/end_user_grpc.py +59 -0
  128. viam/gen/app/v1/end_user_pb2.py +55 -0
  129. viam/gen/app/v1/end_user_pb2.pyi +181 -0
  130. viam/gen/app/v1/robot_grpc.py +16 -1
  131. viam/gen/app/v1/robot_pb2.py +122 -94
  132. viam/gen/app/v1/robot_pb2.pyi +463 -123
  133. viam/gen/common/v1/common_pb2.py +87 -58
  134. viam/gen/common/v1/common_pb2.pyi +456 -149
  135. viam/gen/component/arm/v1/arm_grpc.py +58 -2
  136. viam/gen/component/arm/v1/arm_pb2.py +68 -51
  137. viam/gen/component/arm/v1/arm_pb2.pyi +108 -42
  138. viam/gen/component/audioin/__init__.py +0 -0
  139. viam/gen/component/audioin/v1/__init__.py +0 -0
  140. viam/gen/component/audioin/v1/audioin_grpc.py +54 -0
  141. viam/gen/component/audioin/v1/audioin_pb2.py +34 -0
  142. viam/gen/component/audioin/v1/audioin_pb2.pyi +94 -0
  143. viam/gen/component/audioinput/v1/audioinput_grpc.py +25 -2
  144. viam/gen/component/audioinput/v1/audioinput_pb2.py +36 -31
  145. viam/gen/component/audioinput/v1/audioinput_pb2.pyi +22 -22
  146. viam/gen/component/audioout/__init__.py +0 -0
  147. viam/gen/component/audioout/v1/__init__.py +0 -0
  148. viam/gen/component/audioout/v1/audioout_grpc.py +54 -0
  149. viam/gen/component/audioout/v1/audioout_pb2.py +32 -0
  150. viam/gen/component/audioout/v1/audioout_pb2.pyi +47 -0
  151. viam/gen/component/base/v1/base_grpc.py +42 -2
  152. viam/gen/component/base/v1/base_pb2.py +58 -47
  153. viam/gen/component/base/v1/base_pb2.pyi +65 -30
  154. viam/gen/component/board/v1/board_grpc.py +59 -7
  155. viam/gen/component/board/v1/board_pb2.py +94 -73
  156. viam/gen/component/board/v1/board_pb2.pyi +165 -68
  157. viam/gen/component/button/__init__.py +0 -0
  158. viam/gen/component/button/v1/__init__.py +0 -0
  159. viam/gen/component/button/v1/button_grpc.py +38 -0
  160. viam/gen/component/button/v1/button_pb2.py +28 -0
  161. viam/gen/component/button/v1/button_pb2.pyi +39 -0
  162. viam/gen/component/camera/v1/camera_grpc.py +38 -2
  163. viam/gen/component/camera/v1/camera_pb2.py +60 -43
  164. viam/gen/component/camera/v1/camera_pb2.pyi +191 -37
  165. viam/gen/component/encoder/v1/encoder_grpc.py +25 -2
  166. viam/gen/component/encoder/v1/encoder_pb2.py +36 -31
  167. viam/gen/component/encoder/v1/encoder_pb2.pyi +15 -15
  168. viam/gen/component/gantry/v1/gantry_grpc.py +47 -2
  169. viam/gen/component/gantry/v1/gantry_pb2.py +56 -43
  170. viam/gen/component/gantry/v1/gantry_pb2.pyi +67 -31
  171. viam/gen/component/generic/v1/generic_grpc.py +16 -2
  172. viam/gen/component/generic/v1/generic_pb2.py +16 -11
  173. viam/gen/component/gripper/v1/gripper_grpc.py +44 -2
  174. viam/gen/component/gripper/v1/gripper_pb2.py +48 -35
  175. viam/gen/component/gripper/v1/gripper_pb2.pyi +62 -24
  176. viam/gen/component/inputcontroller/v1/input_controller_grpc.py +28 -2
  177. viam/gen/component/inputcontroller/v1/input_controller_pb2.py +46 -41
  178. viam/gen/component/inputcontroller/v1/input_controller_pb2.pyi +32 -36
  179. viam/gen/component/motor/v1/motor_grpc.py +51 -2
  180. viam/gen/component/motor/v1/motor_pb2.py +78 -67
  181. viam/gen/component/motor/v1/motor_pb2.pyi +75 -46
  182. viam/gen/component/movementsensor/v1/movementsensor_grpc.py +48 -2
  183. viam/gen/component/movementsensor/v1/movementsensor_pb2.py +70 -63
  184. viam/gen/component/movementsensor/v1/movementsensor_pb2.pyi +84 -57
  185. viam/gen/component/posetracker/v1/pose_tracker_grpc.py +19 -2
  186. viam/gen/component/posetracker/v1/pose_tracker_pb2.py +26 -21
  187. viam/gen/component/posetracker/v1/pose_tracker_pb2.pyi +9 -13
  188. viam/gen/component/powersensor/__init__.py +0 -0
  189. viam/gen/component/powersensor/v1/__init__.py +0 -0
  190. viam/gen/component/powersensor/v1/powersensor_grpc.py +62 -0
  191. viam/gen/component/powersensor/v1/powersensor_pb2.py +42 -0
  192. viam/gen/component/powersensor/v1/powersensor_pb2.pyi +124 -0
  193. viam/gen/component/sensor/v1/sensor_grpc.py +21 -5
  194. viam/gen/component/sensor/v1/sensor_pb2.py +18 -22
  195. viam/gen/component/sensor/v1/sensor_pb2.pyi +1 -69
  196. viam/gen/component/servo/v1/servo_grpc.py +28 -2
  197. viam/gen/component/servo/v1/servo_pb2.py +42 -37
  198. viam/gen/component/servo/v1/servo_pb2.pyi +22 -26
  199. viam/gen/component/switch/__init__.py +0 -0
  200. viam/gen/component/switch/v1/__init__.py +0 -0
  201. viam/gen/component/switch/v1/switch_grpc.py +54 -0
  202. viam/gen/component/switch/v1/switch_pb2.py +40 -0
  203. viam/gen/component/switch/v1/switch_pb2.pyi +116 -0
  204. viam/gen/component/testecho/v1/testecho_grpc.py +15 -0
  205. viam/gen/component/testecho/v1/testecho_pb2.py +29 -26
  206. viam/gen/component/testecho/v1/testecho_pb2.pyi +16 -20
  207. viam/gen/module/v1/module_grpc.py +18 -0
  208. viam/gen/module/v1/module_pb2.py +36 -33
  209. viam/gen/module/v1/module_pb2.pyi +39 -34
  210. viam/gen/opentelemetry/__init__.py +0 -0
  211. viam/gen/opentelemetry/proto/__init__.py +0 -0
  212. viam/gen/opentelemetry/proto/common/__init__.py +0 -0
  213. viam/gen/opentelemetry/proto/common/v1/__init__.py +0 -0
  214. viam/gen/opentelemetry/proto/common/v1/common_grpc.py +0 -0
  215. viam/gen/opentelemetry/proto/common/v1/common_pb2.py +27 -0
  216. viam/gen/opentelemetry/proto/common/v1/common_pb2.pyi +208 -0
  217. viam/gen/opentelemetry/proto/resource/__init__.py +0 -0
  218. viam/gen/opentelemetry/proto/resource/v1/__init__.py +0 -0
  219. viam/gen/opentelemetry/proto/resource/v1/resource_grpc.py +0 -0
  220. viam/gen/opentelemetry/proto/resource/v1/resource_pb2.py +18 -0
  221. viam/gen/opentelemetry/proto/resource/v1/resource_pb2.pyi +59 -0
  222. viam/gen/opentelemetry/proto/trace/__init__.py +0 -0
  223. viam/gen/opentelemetry/proto/trace/v1/__init__.py +0 -0
  224. viam/gen/opentelemetry/proto/trace/v1/trace_grpc.py +0 -0
  225. viam/gen/opentelemetry/proto/trace/v1/trace_pb2.py +37 -0
  226. viam/gen/opentelemetry/proto/trace/v1/trace_pb2.pyi +402 -0
  227. viam/gen/proto/rpc/examples/echo/v1/echo_grpc.py +12 -0
  228. viam/gen/proto/rpc/examples/echo/v1/echo_pb2.py +25 -22
  229. viam/gen/proto/rpc/examples/echo/v1/echo_pb2.pyi +13 -17
  230. viam/gen/proto/rpc/examples/echoresource/v1/echoresource_grpc.py +12 -0
  231. viam/gen/proto/rpc/examples/echoresource/v1/echoresource_pb2.py +23 -20
  232. viam/gen/proto/rpc/examples/echoresource/v1/echoresource_pb2.pyi +13 -17
  233. viam/gen/proto/rpc/v1/auth_grpc.py +11 -0
  234. viam/gen/proto/rpc/v1/auth_pb2.py +27 -24
  235. viam/gen/proto/rpc/v1/auth_pb2.pyi +12 -16
  236. viam/gen/proto/rpc/webrtc/v1/grpc_pb2.py +35 -32
  237. viam/gen/proto/rpc/webrtc/v1/grpc_pb2.pyi +37 -41
  238. viam/gen/proto/rpc/webrtc/v1/signaling_grpc.py +15 -0
  239. viam/gen/proto/rpc/webrtc/v1/signaling_pb2.py +62 -57
  240. viam/gen/proto/rpc/webrtc/v1/signaling_pb2.pyi +78 -69
  241. viam/gen/provisioning/__init__.py +0 -0
  242. viam/gen/provisioning/v1/__init__.py +0 -0
  243. viam/gen/provisioning/v1/provisioning_grpc.py +59 -0
  244. viam/gen/provisioning/v1/provisioning_pb2.py +45 -0
  245. viam/gen/provisioning/v1/provisioning_pb2.pyi +229 -0
  246. viam/gen/robot/v1/robot_grpc.py +144 -15
  247. viam/gen/robot/v1/robot_pb2.py +193 -119
  248. viam/gen/robot/v1/robot_pb2.pyi +565 -137
  249. viam/gen/service/datamanager/v1/data_manager_grpc.py +20 -2
  250. viam/gen/service/datamanager/v1/data_manager_pb2.py +27 -17
  251. viam/gen/service/datamanager/v1/data_manager_pb2.pyi +52 -10
  252. viam/gen/service/discovery/__init__.py +0 -0
  253. viam/gen/service/discovery/v1/__init__.py +0 -0
  254. viam/gen/service/discovery/v1/discovery_grpc.py +39 -0
  255. viam/gen/service/discovery/v1/discovery_pb2.py +29 -0
  256. viam/gen/service/discovery/v1/discovery_pb2.pyi +51 -0
  257. viam/gen/service/generic/__init__.py +0 -0
  258. viam/gen/service/generic/v1/__init__.py +0 -0
  259. viam/gen/service/generic/v1/generic_grpc.py +29 -0
  260. viam/gen/service/generic/v1/generic_pb2.py +21 -0
  261. viam/gen/service/generic/v1/generic_pb2.pyi +6 -0
  262. viam/gen/service/mlmodel/v1/mlmodel_grpc.py +9 -0
  263. viam/gen/service/mlmodel/v1/mlmodel_pb2.py +76 -29
  264. viam/gen/service/mlmodel/v1/mlmodel_pb2.pyi +307 -28
  265. viam/gen/service/motion/v1/motion_grpc.py +42 -4
  266. viam/gen/service/motion/v1/motion_pb2.py +119 -51
  267. viam/gen/service/motion/v1/motion_pb2.pyi +595 -120
  268. viam/gen/service/navigation/v1/navigation_grpc.py +49 -1
  269. viam/gen/service/navigation/v1/navigation_pb2.py +76 -51
  270. viam/gen/service/navigation/v1/navigation_pb2.pyi +188 -33
  271. viam/gen/service/sensors/v1/sensors_grpc.py +12 -0
  272. viam/gen/service/sensors/v1/sensors_pb2.py +60 -29
  273. viam/gen/service/sensors/v1/sensors_pb2.pyi +18 -21
  274. viam/gen/service/shell/v1/shell_grpc.py +27 -1
  275. viam/gen/service/shell/v1/shell_pb2.py +37 -15
  276. viam/gen/service/shell/v1/shell_pb2.pyi +260 -7
  277. viam/gen/service/slam/v1/slam_grpc.py +24 -2
  278. viam/gen/service/slam/v1/slam_pb2.py +44 -30
  279. viam/gen/service/slam/v1/slam_pb2.pyi +128 -27
  280. viam/gen/service/video/__init__.py +0 -0
  281. viam/gen/service/video/v1/__init__.py +0 -0
  282. viam/gen/service/video/v1/video_grpc.py +39 -0
  283. viam/gen/service/video/v1/video_pb2.py +29 -0
  284. viam/gen/service/video/v1/video_pb2.pyi +72 -0
  285. viam/gen/service/vision/v1/vision_grpc.py +39 -1
  286. viam/gen/service/vision/v1/vision_pb2.py +61 -45
  287. viam/gen/service/vision/v1/vision_pb2.pyi +180 -41
  288. viam/gen/service/worldstatestore/__init__.py +0 -0
  289. viam/gen/service/worldstatestore/v1/__init__.py +0 -0
  290. viam/gen/service/worldstatestore/v1/world_state_store_grpc.py +55 -0
  291. viam/gen/service/worldstatestore/v1/world_state_store_pb2.py +39 -0
  292. viam/gen/service/worldstatestore/v1/world_state_store_pb2.pyi +171 -0
  293. viam/gen/stream/__init__.py +0 -0
  294. viam/gen/stream/v1/__init__.py +0 -0
  295. viam/gen/stream/v1/stream_grpc.py +59 -0
  296. viam/gen/stream/v1/stream_pb2.py +39 -0
  297. viam/gen/stream/v1/stream_pb2.pyi +161 -0
  298. viam/gen/tagger/v1/tagger_pb2.py +9 -8
  299. viam/logging.py +160 -17
  300. viam/media/__init__.py +0 -9
  301. viam/media/audio.py +22 -10
  302. viam/media/utils/__init__.py +0 -0
  303. viam/media/utils/pil/__init__.py +55 -0
  304. viam/media/{viam_rgba_plugin.py → utils/pil/viam_rgba_plugin.py} +10 -16
  305. viam/media/viam_rgba.py +10 -0
  306. viam/media/video.py +197 -73
  307. viam/module/module.py +191 -44
  308. viam/module/resource_data_consumer.py +41 -0
  309. viam/module/service.py +9 -1
  310. viam/module/types.py +4 -5
  311. viam/operations.py +4 -3
  312. viam/proto/app/__init__.py +361 -5
  313. viam/proto/app/agent/__init__.py +28 -0
  314. viam/proto/app/billing.py +51 -27
  315. viam/proto/app/cloudslam/__init__.py +48 -0
  316. viam/proto/app/data/__init__.py +103 -17
  317. viam/proto/app/datapipelines/__init__.py +56 -0
  318. viam/proto/app/dataset/__init__.py +40 -0
  319. viam/proto/app/datasync/__init__.py +11 -5
  320. viam/proto/app/end_user.py +34 -0
  321. viam/proto/app/mlinference/__init__.py +15 -0
  322. viam/proto/app/mltraining/__init__.py +25 -1
  323. viam/proto/app/packages/__init__.py +3 -3
  324. viam/proto/app/robot.py +19 -1
  325. viam/proto/common/__init__.py +35 -8
  326. viam/proto/component/arm/__init__.py +9 -1
  327. viam/proto/component/audioin/__init__.py +16 -0
  328. viam/proto/component/audioinput/__init__.py +3 -1
  329. viam/proto/component/audioout/__init__.py +15 -0
  330. viam/proto/component/base/__init__.py +7 -1
  331. viam/proto/component/board/__init__.py +13 -5
  332. viam/proto/component/button/__init__.py +15 -0
  333. viam/proto/component/camera/__init__.py +9 -1
  334. viam/proto/component/encoder/__init__.py +3 -1
  335. viam/proto/component/gantry/__init__.py +7 -1
  336. viam/proto/component/generic/__init__.py +3 -1
  337. viam/proto/component/gripper/__init__.py +7 -1
  338. viam/proto/component/inputcontroller/__init__.py +7 -1
  339. viam/proto/component/motor/__init__.py +7 -1
  340. viam/proto/component/movementsensor/__init__.py +7 -1
  341. viam/proto/component/posetracker/__init__.py +7 -1
  342. viam/proto/component/powersensor/__init__.py +30 -0
  343. viam/proto/component/sensor/__init__.py +3 -4
  344. viam/proto/component/servo/__init__.py +3 -1
  345. viam/proto/component/switch/__init__.py +26 -0
  346. viam/proto/component/testecho/__init__.py +3 -1
  347. viam/proto/module/__init__.py +3 -1
  348. viam/proto/opentelemetry/__init__.py +0 -0
  349. viam/proto/opentelemetry/proto/__init__.py +0 -0
  350. viam/proto/opentelemetry/proto/common/__init__.py +15 -0
  351. viam/proto/opentelemetry/proto/resource/__init__.py +10 -0
  352. viam/proto/opentelemetry/proto/trace/__init__.py +15 -0
  353. viam/proto/provisioning/__init__.py +42 -0
  354. viam/proto/robot/__init__.py +57 -9
  355. viam/proto/rpc/auth.py +11 -1
  356. viam/proto/rpc/examples/echo/__init__.py +3 -1
  357. viam/proto/rpc/examples/echoresource/__init__.py +7 -1
  358. viam/proto/rpc/webrtc/grpc.py +3 -1
  359. viam/proto/rpc/webrtc/signaling.py +5 -1
  360. viam/proto/service/datamanager/__init__.py +15 -2
  361. viam/proto/service/discovery/__init__.py +15 -0
  362. viam/proto/service/generic/__init__.py +12 -0
  363. viam/proto/service/mlmodel/__init__.py +27 -1
  364. viam/proto/service/motion/__init__.py +35 -5
  365. viam/proto/service/navigation/__init__.py +19 -1
  366. viam/proto/service/sensors/__init__.py +3 -1
  367. viam/proto/service/shell/__init__.py +25 -2
  368. viam/proto/service/slam/__init__.py +13 -1
  369. viam/proto/service/video/__init__.py +15 -0
  370. viam/proto/service/vision/__init__.py +11 -1
  371. viam/proto/service/worldstatestore/__init__.py +32 -0
  372. viam/proto/stream/__init__.py +36 -0
  373. viam/py.typed +0 -0
  374. viam/resource/base.py +45 -8
  375. viam/resource/easy_resource.py +149 -0
  376. viam/resource/manager.py +35 -14
  377. viam/resource/registry.py +40 -52
  378. viam/resource/rpc_client_base.py +33 -1
  379. viam/resource/rpc_service_base.py +15 -8
  380. viam/resource/types.py +39 -26
  381. viam/robot/client.py +458 -91
  382. viam/robot/service.py +13 -107
  383. viam/rpc/dial.py +133 -15
  384. viam/rpc/libviam_rust_utils.so +0 -0
  385. viam/rpc/server.py +59 -15
  386. viam/rpc/types.py +2 -4
  387. viam/services/discovery/__init__.py +12 -0
  388. viam/services/discovery/client.py +55 -0
  389. viam/services/discovery/discovery.py +52 -0
  390. viam/services/discovery/service.py +43 -0
  391. viam/services/generic/__init__.py +18 -0
  392. viam/services/generic/client.py +58 -0
  393. viam/services/generic/generic.py +58 -0
  394. viam/services/generic/service.py +29 -0
  395. viam/services/mlmodel/__init__.py +15 -1
  396. viam/services/mlmodel/client.py +20 -15
  397. viam/services/mlmodel/mlmodel.py +44 -7
  398. viam/services/mlmodel/service.py +9 -13
  399. viam/services/mlmodel/utils.py +101 -0
  400. viam/services/motion/__init__.py +15 -3
  401. viam/services/motion/client.py +109 -150
  402. viam/services/motion/motion.py +380 -0
  403. viam/services/motion/service.py +132 -0
  404. viam/services/navigation/__init__.py +11 -0
  405. viam/services/navigation/client.py +99 -0
  406. viam/services/navigation/navigation.py +250 -0
  407. viam/services/navigation/service.py +137 -0
  408. viam/services/service_base.py +43 -4
  409. viam/services/service_client_base.py +4 -4
  410. viam/services/slam/__init__.py +4 -1
  411. viam/services/slam/client.py +21 -11
  412. viam/services/slam/service.py +16 -19
  413. viam/services/slam/slam.py +66 -5
  414. viam/services/vision/__init__.py +8 -0
  415. viam/services/vision/client.py +115 -111
  416. viam/services/vision/service.py +143 -0
  417. viam/services/vision/vision.py +317 -0
  418. viam/services/worldstatestore/__init__.py +18 -0
  419. viam/services/worldstatestore/client.py +94 -0
  420. viam/services/worldstatestore/service.py +55 -0
  421. viam/services/worldstatestore/worldstatestore.py +90 -0
  422. viam/sessions_client.py +254 -0
  423. viam/streams.py +44 -0
  424. viam/utils.py +143 -15
  425. viam/version_metadata.py +4 -0
  426. viam_sdk-0.66.0.dist-info/METADATA +157 -0
  427. viam_sdk-0.66.0.dist-info/RECORD +531 -0
  428. {viam_sdk-0.3.0.dist-info → viam_sdk-0.66.0.dist-info}/WHEEL +1 -1
  429. viam/components/audio_input/__init__.py +0 -18
  430. viam/components/audio_input/audio_input.py +0 -79
  431. viam/components/audio_input/client.py +0 -60
  432. viam/components/audio_input/service.py +0 -118
  433. viam/components/types.py +0 -5
  434. viam/gen/app/model/v1/model_grpc.py +0 -39
  435. viam/gen/app/model/v1/model_pb2.py +0 -71
  436. viam/gen/app/model/v1/model_pb2.pyi +0 -285
  437. viam/gen/proto/rpc/examples/fileupload/v1/fileupload_grpc.py +0 -21
  438. viam/gen/proto/rpc/examples/fileupload/v1/fileupload_pb2.py +0 -18
  439. viam/gen/proto/rpc/examples/fileupload/v1/fileupload_pb2.pyi +0 -49
  440. viam/media/media.py +0 -53
  441. viam/proto/app/model/__init__.py +0 -40
  442. viam/proto/rpc/examples/fileupload/__init__.py +0 -13
  443. viam/services/sensors/__init__.py +0 -5
  444. viam/services/sensors/client.py +0 -63
  445. viam_sdk-0.3.0.dist-info/LICENSE +0 -202
  446. viam_sdk-0.3.0.dist-info/METADATA +0 -122
  447. viam_sdk-0.3.0.dist-info/RECORD +0 -372
  448. /viam/{gen/app/model → app}/__init__.py +0 -0
  449. /viam/gen/app/{model/v1 → agent}/__init__.py +0 -0
  450. /viam/gen/{proto/rpc/examples/fileupload → app/agent/v1}/__init__.py +0 -0
  451. /viam/gen/{proto/rpc/examples/fileupload/v1 → app/cloudslam}/__init__.py +0 -0
  452. /LICENSE → /viam_sdk-0.66.0.dist-info/licenses/LICENSE +0 -0
@@ -0,0 +1,2231 @@
1
+ import warnings
2
+ from dataclasses import dataclass
3
+ from datetime import datetime
4
+ from pathlib import Path
5
+ from typing import Any, Dict, List, Mapping, Optional, Sequence, Tuple, Union, cast
6
+
7
+ import bson
8
+ from google.protobuf.struct_pb2 import Struct
9
+ from grpclib.client import Channel, Stream
10
+ from typing_extensions import Self
11
+
12
+ from viam import logging
13
+ from viam.proto.app.data import (
14
+ AddBinaryDataToDatasetByIDsRequest,
15
+ AddBoundingBoxToImageByIDRequest,
16
+ AddBoundingBoxToImageByIDResponse,
17
+ AddTagsToBinaryDataByFilterRequest,
18
+ AddTagsToBinaryDataByIDsRequest,
19
+ BinaryData,
20
+ BinaryDataByFilterRequest,
21
+ BinaryDataByFilterResponse,
22
+ BinaryDataByIDsRequest,
23
+ BinaryDataByIDsResponse,
24
+ BinaryID,
25
+ BoundingBoxLabelsByFilterRequest,
26
+ BoundingBoxLabelsByFilterResponse,
27
+ CaptureInterval,
28
+ CaptureMetadata,
29
+ ConfigureDatabaseUserRequest,
30
+ CreateBinaryDataSignedURLRequest,
31
+ CreateBinaryDataSignedURLResponse,
32
+ CreateIndexRequest,
33
+ DataRequest,
34
+ DataServiceStub,
35
+ DeleteBinaryDataByFilterRequest,
36
+ DeleteBinaryDataByFilterResponse,
37
+ DeleteBinaryDataByIDsRequest,
38
+ DeleteBinaryDataByIDsResponse,
39
+ DeleteIndexRequest,
40
+ DeleteTabularDataRequest,
41
+ DeleteTabularDataResponse,
42
+ ExportTabularDataRequest,
43
+ ExportTabularDataResponse,
44
+ Filter,
45
+ GetDatabaseConnectionRequest,
46
+ GetDatabaseConnectionResponse,
47
+ GetLatestTabularDataRequest,
48
+ GetLatestTabularDataResponse,
49
+ Index,
50
+ IndexableCollection,
51
+ ListIndexesRequest,
52
+ ListIndexesResponse,
53
+ Order,
54
+ RemoveBinaryDataFromDatasetByIDsRequest,
55
+ RemoveBoundingBoxFromImageByIDRequest,
56
+ RemoveTagsFromBinaryDataByFilterRequest,
57
+ RemoveTagsFromBinaryDataByFilterResponse,
58
+ RemoveTagsFromBinaryDataByIDsRequest,
59
+ RemoveTagsFromBinaryDataByIDsResponse,
60
+ TabularDataByFilterRequest,
61
+ TabularDataByFilterResponse,
62
+ TabularDataByMQLRequest,
63
+ TabularDataByMQLResponse,
64
+ TabularDataBySQLRequest,
65
+ TabularDataBySQLResponse,
66
+ TabularDataSource,
67
+ TabularDataSourceType,
68
+ TagsByFilterRequest,
69
+ TagsByFilterResponse,
70
+ )
71
+ from viam.proto.app.datapipelines import (
72
+ CreateDataPipelineRequest,
73
+ CreateDataPipelineResponse,
74
+ DataPipelineRunStatus,
75
+ DataPipelinesServiceStub,
76
+ DeleteDataPipelineRequest,
77
+ GetDataPipelineRequest,
78
+ GetDataPipelineResponse,
79
+ ListDataPipelineRunsRequest,
80
+ ListDataPipelineRunsResponse,
81
+ ListDataPipelinesRequest,
82
+ ListDataPipelinesResponse,
83
+ RenameDataPipelineRequest,
84
+ )
85
+ from viam.proto.app.datapipelines import (
86
+ DataPipeline as ProtoDataPipeline,
87
+ )
88
+ from viam.proto.app.datapipelines import (
89
+ DataPipelineRun as ProtoDataPipelineRun,
90
+ )
91
+ from viam.proto.app.dataset import (
92
+ CreateDatasetRequest,
93
+ CreateDatasetResponse,
94
+ Dataset,
95
+ DatasetServiceStub,
96
+ DeleteDatasetRequest,
97
+ ListDatasetsByIDsRequest,
98
+ ListDatasetsByIDsResponse,
99
+ ListDatasetsByOrganizationIDRequest,
100
+ ListDatasetsByOrganizationIDResponse,
101
+ MergeDatasetsRequest,
102
+ MergeDatasetsResponse,
103
+ RenameDatasetRequest,
104
+ )
105
+ from viam.proto.app.datasync import (
106
+ DataCaptureUploadMetadata,
107
+ DataCaptureUploadRequest,
108
+ DataCaptureUploadResponse,
109
+ DataSyncServiceStub,
110
+ DataType,
111
+ FileData,
112
+ FileUploadRequest,
113
+ FileUploadResponse,
114
+ SensorData,
115
+ SensorMetadata,
116
+ StreamingDataCaptureUploadRequest,
117
+ StreamingDataCaptureUploadResponse,
118
+ UploadMetadata,
119
+ )
120
+ from viam.utils import ValueTypes, _alias_param, create_filter, datetime_to_timestamp, dict_to_struct, struct_to_dict
121
+
122
+ LOGGER = logging.getLogger(__name__)
123
+
124
+
125
+ class DataClient:
126
+ """gRPC client for uploading and retrieving data from app.
127
+
128
+ This class's constructor instantiates relevant service stubs. Always make :class:`DataClient` method calls through an instance of
129
+ :class:`ViamClient`.
130
+
131
+ Establish a connection::
132
+
133
+ import asyncio
134
+
135
+ from viam.rpc.dial import DialOptions, Credentials
136
+ from viam.app.viam_client import ViamClient
137
+
138
+
139
+ async def connect() -> ViamClient:
140
+ # Replace "<API-KEY>" (including brackets) with your API key and "<API-KEY-ID>" with your API key ID
141
+ dial_options = DialOptions.with_api_key("<API-KEY>", "<API-KEY-ID>")
142
+ return await ViamClient.create_from_dial_options(dial_options)
143
+
144
+
145
+ async def main():
146
+ # Make a ViamClient
147
+ async with await connect() as viam_client:
148
+ # Instantiate a DataClient to run data client API methods on
149
+ data_client = viam_client.data_client
150
+
151
+ if __name__ == '__main__':
152
+ asyncio.run(main())
153
+
154
+
155
+ For more information, see `Data Client API <https://docs.viam.com/dev/reference/apis/data-client/>`_.
156
+ """
157
+
158
+ @dataclass
159
+ class TabularData:
160
+ """Class representing a piece of tabular data and associated metadata."""
161
+
162
+ data: Mapping[str, Any]
163
+ """The requested data"""
164
+
165
+ metadata: CaptureMetadata
166
+ """The metadata associated with the data"""
167
+
168
+ time_requested: datetime
169
+ """The time the data were requested"""
170
+
171
+ time_received: datetime
172
+ """The time the data were received"""
173
+
174
+ def __str__(self) -> str:
175
+ return f"{self.data}\n{self.metadata}Time requested: {self.time_requested}\nTime received: {self.time_received}\n"
176
+
177
+ def __eq__(self, other: object) -> bool:
178
+ if isinstance(other, DataClient.TabularData):
179
+ return str(self) == str(other)
180
+ return False
181
+
182
+ @dataclass
183
+ class TabularDataPoint:
184
+ """Represents a tabular data point and its associated metadata."""
185
+
186
+ part_id: str
187
+ """The robot part ID"""
188
+
189
+ resource_name: str
190
+ """The resource name"""
191
+
192
+ resource_api: str
193
+ """The resource API. For example, rdk:component:sensor"""
194
+
195
+ method_name: str
196
+ """The method used for data capture. For example, Readings"""
197
+
198
+ time_captured: datetime
199
+ """The time at which the data point was captured"""
200
+
201
+ organization_id: str
202
+ """The organization ID"""
203
+
204
+ location_id: str
205
+ """The location ID"""
206
+
207
+ robot_name: str
208
+ """The robot name"""
209
+
210
+ robot_id: str
211
+ """The robot ID"""
212
+
213
+ part_name: str
214
+ """The robot part name"""
215
+
216
+ method_parameters: Mapping[str, ValueTypes]
217
+ """Additional parameters associated with the data capture method"""
218
+
219
+ tags: List[str]
220
+ """A list of tags associated with the data point"""
221
+
222
+ payload: Mapping[str, ValueTypes]
223
+ """The captured data"""
224
+
225
+ def __str__(self) -> str:
226
+ return (
227
+ f"TabularDataPoint("
228
+ f"robot='{self.robot_name}' (id={self.robot_id}), "
229
+ f"part='{self.part_name}' (id={self.part_id}), "
230
+ f"resource='{self.resource_name}' ({self.resource_api}), "
231
+ f"method='{self.method_name}', "
232
+ f"org={self.organization_id}, "
233
+ f"location={self.location_id}, "
234
+ f"time='{self.time_captured.isoformat()}', "
235
+ f"params={self.method_parameters}, "
236
+ f"tags={self.tags}, "
237
+ f"payload={self.payload})"
238
+ )
239
+
240
+ def __eq__(self, other: object) -> bool:
241
+ if isinstance(other, DataClient.TabularDataPoint):
242
+ return str(self) == str(other)
243
+ return False
244
+
245
+ @property
246
+ def resource_subtype(self) -> str:
247
+ warnings.warn(
248
+ "`TabularDataPoint.resource_subtype` is deprecated. Use `TabularDataPoint.resource_api` instead.",
249
+ DeprecationWarning,
250
+ stacklevel=2,
251
+ )
252
+ return self.resource_api
253
+
254
+ @dataclass
255
+ class DataPipeline:
256
+ """Represents a data pipeline and its associated metadata."""
257
+
258
+ id: str
259
+ """The ID of the data pipeline"""
260
+
261
+ organization_id: str
262
+ """The organization ID"""
263
+
264
+ name: str
265
+ """The name of the data pipeline"""
266
+
267
+ mql_binary: List[Dict[str, Any]]
268
+ """The MQL binary of the data pipeline"""
269
+
270
+ schedule: str
271
+ """The schedule of the data pipeline"""
272
+
273
+ created_on: datetime
274
+ """The time the data pipeline was created"""
275
+
276
+ updated_at: datetime
277
+ """The time the data pipeline was last updated"""
278
+
279
+ enabled: bool
280
+ """Whether the data pipeline is enabled"""
281
+
282
+ data_source_type: TabularDataSourceType.ValueType
283
+ """The type of data source for the data pipeline"""
284
+
285
+ @classmethod
286
+ def from_proto(cls, data_pipeline: ProtoDataPipeline) -> Self:
287
+ return cls(
288
+ id=data_pipeline.id,
289
+ organization_id=data_pipeline.organization_id,
290
+ name=data_pipeline.name,
291
+ mql_binary=[bson.decode(bson_bytes) for bson_bytes in data_pipeline.mql_binary],
292
+ schedule=data_pipeline.schedule,
293
+ created_on=data_pipeline.created_on.ToDatetime(),
294
+ updated_at=data_pipeline.updated_at.ToDatetime(),
295
+ enabled=data_pipeline.enabled,
296
+ data_source_type=data_pipeline.data_source_type,
297
+ )
298
+
299
+ @dataclass
300
+ class DataPipelineRun:
301
+ """Represents a data pipeline run and its associated metadata."""
302
+
303
+ id: str
304
+ """The ID of the data pipeline run"""
305
+
306
+ status: DataPipelineRunStatus.ValueType
307
+ """The status of the data pipeline run"""
308
+
309
+ start_time: datetime
310
+ """The time the data pipeline run started"""
311
+
312
+ end_time: datetime
313
+ """The time the data pipeline run ended"""
314
+
315
+ data_start_time: datetime
316
+ """The start time of the data that was processed in the run."""
317
+ data_end_time: datetime
318
+ """The end time of the data that was processed in the run."""
319
+
320
+ error_message: str
321
+ """The error message of the data pipeline run. Only set if the run failed."""
322
+
323
+ @classmethod
324
+ def from_proto(cls, data_pipeline_run: ProtoDataPipelineRun) -> Self:
325
+ return cls(
326
+ id=data_pipeline_run.id,
327
+ status=data_pipeline_run.status,
328
+ start_time=data_pipeline_run.start_time.ToDatetime(),
329
+ end_time=data_pipeline_run.end_time.ToDatetime(),
330
+ data_start_time=data_pipeline_run.data_start_time.ToDatetime(),
331
+ data_end_time=data_pipeline_run.data_end_time.ToDatetime(),
332
+ error_message=data_pipeline_run.error_message,
333
+ )
334
+
335
+ @dataclass
336
+ class DataPipelineRunsPage:
337
+ """Represents a page of data pipeline runs and provides pagination functionality."""
338
+
339
+ _client: "DataClient"
340
+ """The data client used to make API calls"""
341
+
342
+ pipeline_id: str
343
+ """The ID of the pipeline these runs belong to"""
344
+
345
+ page_size: int
346
+ """The number of runs per page"""
347
+
348
+ runs: List["DataClient.DataPipelineRun"]
349
+ """The list of runs in this page"""
350
+
351
+ next_page_token: str
352
+ """The token to use to get the next page of results"""
353
+
354
+ async def next_page(self) -> "DataClient.DataPipelineRunsPage":
355
+ """Get the next page of data pipeline runs.
356
+
357
+ Returns:
358
+ DataPipelineRunsPage: The next page of runs, or an empty page if there are no more runs
359
+ """
360
+ if not self.next_page_token:
361
+ # no token, return empty next page
362
+ return DataClient.DataPipelineRunsPage(
363
+ _client=self._client, pipeline_id=self.pipeline_id, page_size=self.page_size, runs=[], next_page_token=""
364
+ )
365
+ return await self._client._list_data_pipeline_runs(self.pipeline_id, self.page_size, self.next_page_token)
366
+
367
+ @classmethod
368
+ def from_proto(cls, data_pipeline_runs_page: ListDataPipelineRunsResponse, client: "DataClient", page_size: int) -> Self:
369
+ return cls(
370
+ _client=client,
371
+ pipeline_id=data_pipeline_runs_page.pipeline_id,
372
+ page_size=page_size,
373
+ runs=[DataClient.DataPipelineRun.from_proto(run) for run in data_pipeline_runs_page.runs],
374
+ next_page_token=data_pipeline_runs_page.next_page_token,
375
+ )
376
+
377
+ def __init__(self, channel: Channel, metadata: Mapping[str, str]):
378
+ """Create a :class:`DataClient` that maintains a connection to app.
379
+
380
+ Args:
381
+ channel (grpclib.client.Channel): Connection to app.
382
+ metadata (Mapping[str, str]): Required authorization token to send requests to app.
383
+ """
384
+ self._metadata = metadata
385
+ self._data_client = DataServiceStub(channel)
386
+ self._data_sync_client = DataSyncServiceStub(channel)
387
+ self._dataset_client = DatasetServiceStub(channel)
388
+ self._data_pipelines_client = DataPipelinesServiceStub(channel)
389
+ self._channel = channel
390
+
391
+ _data_client: DataServiceStub
392
+ _data_sync_client: DataSyncServiceStub
393
+ _dataset_client: DatasetServiceStub
394
+ _data_pipelines_client: DataPipelinesServiceStub
395
+ _metadata: Mapping[str, str]
396
+ _channel: Channel
397
+
398
+ async def tabular_data_by_filter(
399
+ self,
400
+ filter: Optional[Filter] = None,
401
+ limit: Optional[int] = None,
402
+ sort_order: Optional[Order.ValueType] = None,
403
+ last: Optional[str] = None,
404
+ count_only: bool = False,
405
+ include_internal_data: bool = False,
406
+ dest: Optional[str] = None,
407
+ ) -> Tuple[List[TabularData], int, str]:
408
+ """Filter and download tabular data. The data will be paginated into pages of ``limit`` items; the returned tuple will include
409
+ the pagination ID. If a destination is provided, this method saves returned data to that file, overwriting any existing file content.
410
+
411
+ ::
412
+
413
+ from viam.utils import create_filter
414
+
415
+ my_data = []
416
+ my_filter = create_filter(component_name="motor-1")
417
+ last = None
418
+ while True:
419
+ tabular_data, count, last = await data_client.tabular_data_by_filter(my_filter, last=last)
420
+ if not tabular_data:
421
+ break
422
+ my_data.extend(tabular_data)
423
+
424
+ print(f"My data: {my_data}")
425
+
426
+ Args:
427
+ filter (~viam.proto.app.data.Filter): Optional, specifies tabular data to retrieve. If missing, matches all tabular data.
428
+ limit (int): The maximum number of entries to include in a page. Defaults to 50 if unspecified.
429
+ sort_order (~viam.proto.app.data.Order): The desired sort order of the data.
430
+ last (str): Optional string indicating the object identifier of the last-returned data.
431
+ Returned by calls to :class:`TabularDataByFilter` as the ``last`` value.
432
+ If provided, the server returns the next data entries after the last object identifier.
433
+ count_only (bool): Whether to return only the total count of entries.
434
+ include_internal_data (bool): Whether to return the internal data. Internal data is used for Viam-specific data ingestion,
435
+ like cloud SLAM. Defaults to ``False``.
436
+ dest (str): Optional filepath for writing retrieved data.
437
+
438
+ Returns:
439
+ Tuple[List[TabularData], int, str]: A tuple containing the following:
440
+
441
+ - ``tabular_data`` (*List[TabularData]*): The tabular data.
442
+ - ``count`` (*int*): The count (number of entries).
443
+ - ``last`` (*str*): The last-returned page ID.
444
+
445
+ For more information, see `Data Client API <https://docs.viam.com/dev/reference/apis/data-client/#tabulardatabyfilter>`_.
446
+ """
447
+ filter = filter if filter else Filter()
448
+
449
+ data_request = DataRequest(filter=filter)
450
+ if limit:
451
+ data_request.limit = limit
452
+ if sort_order:
453
+ data_request.sort_order = sort_order
454
+ if last:
455
+ data_request.last = last
456
+ request = TabularDataByFilterRequest(data_request=data_request, count_only=count_only, include_internal_data=include_internal_data)
457
+ response: TabularDataByFilterResponse = await self._data_client.TabularDataByFilter(request, metadata=self._metadata)
458
+ data = [
459
+ DataClient.TabularData(
460
+ struct_to_dict(struct.data),
461
+ response.metadata[struct.metadata_index],
462
+ struct.time_requested.ToDatetime(),
463
+ struct.time_received.ToDatetime(),
464
+ )
465
+ for struct in response.data
466
+ ]
467
+
468
+ if dest:
469
+ try:
470
+ file = open(dest, "w")
471
+ file.write(f"{[str(d) for d in data]}")
472
+ file.flush()
473
+ except Exception as e:
474
+ LOGGER.error(f"Failed to write tabular data to file {dest}", exc_info=e)
475
+ return data, response.count, response.last
476
+
477
+ async def tabular_data_by_sql(self, organization_id: str, sql_query: str) -> List[Dict[str, Union[ValueTypes, datetime]]]:
478
+ """Obtain unified tabular data and metadata, queried with SQL.
479
+ Make sure your API key has permissions at the organization level in order to use this.
480
+
481
+ ::
482
+
483
+ data = await data_client.tabular_data_by_sql(
484
+ organization_id="<YOUR-ORG-ID>",
485
+ sql_query="SELECT * FROM readings LIMIT 5"
486
+ )
487
+
488
+ Args:
489
+ organization_id (str): The ID of the organization that owns the data.
490
+ To find your organization ID, visit the organization settings page.
491
+ sql_query (str): The SQL query to run.
492
+
493
+ Returns:
494
+ List[Dict[str, Union[ValueTypes, datetime]]]: An array of decoded BSON data objects.
495
+
496
+ For more information, see `Data Client API <https://docs.viam.com/dev/reference/apis/data-client/#tabulardatabysql>`_.
497
+ """
498
+ request = TabularDataBySQLRequest(organization_id=organization_id, sql_query=sql_query)
499
+ response: TabularDataBySQLResponse = await self._data_client.TabularDataBySQL(request, metadata=self._metadata)
500
+ return [bson.decode(bson_bytes) for bson_bytes in response.raw_data]
501
+
502
+ @_alias_param("query", param_alias="mql_binary")
503
+ async def tabular_data_by_mql(
504
+ self,
505
+ organization_id: str,
506
+ query: Union[List[bytes], List[Dict[str, Any]]],
507
+ use_recent_data: Optional[bool] = None,
508
+ tabular_data_source_type: TabularDataSourceType.ValueType = TabularDataSourceType.TABULAR_DATA_SOURCE_TYPE_STANDARD,
509
+ pipeline_id: Optional[str] = None,
510
+ query_prefix_name: Optional[str] = None,
511
+ ) -> List[Dict[str, Union[ValueTypes, datetime]]]:
512
+ """Obtain unified tabular data and metadata, queried with MQL.
513
+
514
+ ::
515
+
516
+ import bson
517
+
518
+ tabular_data = await data_client.tabular_data_by_mql(organization_id="<YOUR-ORG-ID>", query=[
519
+ { '$match': { 'location_id': '<YOUR-LOCATION-ID>' } },
520
+ { "$limit": 5 }
521
+ ])
522
+
523
+ print(f"Tabular Data: {tabular_data}")
524
+
525
+ Args:
526
+ organization_id (str): The ID of the organization that owns the data.
527
+ To find your organization ID, visit the organization settings page.
528
+ query (Union[List[bytes], List[Dict[str, Any]]]): The MQL query to run, as a list of MongoDB aggregation pipeline stages.
529
+ Each stage can be provided as either a dictionary or raw BSON bytes, but support for bytes will be removed in the
530
+ future, so prefer the dictionary option.
531
+ use_recent_data (bool): Whether to query blob storage or your recent data store. Defaults to ``False``..
532
+ Deprecated, use `tabular_data_source_type` instead.
533
+ tabular_data_source_type (viam.proto.app.data.TabularDataSourceType): The data source to query.
534
+ Defaults to `TABULAR_DATA_SOURCE_TYPE_STANDARD`.
535
+ pipeline_id (str): The ID of the data pipeline to query. Defaults to `None`.
536
+ Required if `tabular_data_source_type` is `TABULAR_DATA_SOURCE_TYPE_PIPELINE_SINK`.
537
+ query_prefix_name (str): Optional field that can be used to specify a saved query to run.
538
+
539
+ Returns:
540
+ List[Dict[str, Union[ValueTypes, datetime]]]: An array of decoded BSON data objects.
541
+
542
+ For more information, see `Data Client API <https://docs.viam.com/dev/reference/apis/data-client/#tabulardatabymql>`_.
543
+ """
544
+ binary: List[bytes] = [bson.encode(query) for query in query] if isinstance(query[0], dict) else query # type: ignore
545
+ data_source = TabularDataSource(type=tabular_data_source_type, pipeline_id=pipeline_id)
546
+ if use_recent_data:
547
+ data_source.type = TabularDataSourceType.TABULAR_DATA_SOURCE_TYPE_HOT_STORAGE
548
+ request = TabularDataByMQLRequest(
549
+ organization_id=organization_id,
550
+ mql_binary=binary,
551
+ data_source=data_source,
552
+ query_prefix_name=query_prefix_name,
553
+ )
554
+ response: TabularDataByMQLResponse = await self._data_client.TabularDataByMQL(request, metadata=self._metadata)
555
+ return [bson.decode(bson_bytes) for bson_bytes in response.raw_data]
556
+
557
+ @_alias_param("resource_api", param_alias="resource_subtype")
558
+ async def get_latest_tabular_data(
559
+ self,
560
+ part_id: str,
561
+ resource_name: str,
562
+ resource_api: str,
563
+ method_name: str,
564
+ additional_params: Optional[Mapping[str, ValueTypes]] = None,
565
+ ) -> Optional[Tuple[datetime, datetime, Dict[str, ValueTypes]]]:
566
+ """Gets the most recent tabular data captured from the specified data source, as long as it was synced within the last year.
567
+
568
+ ::
569
+
570
+ tabular_data = await data_client.get_latest_tabular_data(
571
+ part_id="77ae3145-7b91-123a-a234-e567cdca8910",
572
+ resource_name="camera-1",
573
+ resource_api="rdk:component:camera",
574
+ method_name="GetImages",
575
+ additional_params={"docommand_input": {"test": "test"}}
576
+ )
577
+
578
+ if tabular_data:
579
+ time_captured, time_synced, payload = tabular_data
580
+ print(f"Time Captured: {time_captured}")
581
+ print(f"Time Synced: {time_synced}")
582
+ print(f"Payload: {payload}")
583
+ else:
584
+ print(f"No data returned: {tabular_data}")
585
+
586
+ Args:
587
+ part_id (str): The ID of the part that owns the data.
588
+ resource_name (str): The name of the requested resource that captured the data. For example, "my-sensor".
589
+ resource_api (str): The API of the requested resource that captured the data. For example, "rdk:component:sensor".
590
+ method_name (str): The data capture method name. For exampe, "Readings".
591
+ additional_params (dict): Optional additional parameters of the resource that captured the data.
592
+
593
+ Returns:
594
+ Optional[Tuple[datetime, datetime, Dict[str, ValueTypes]]]:
595
+ A return value of ``None`` means that this data source
596
+ has not synced data in the last year. Otherwise, the data source has synced some data in the last year, so the returned
597
+ tuple contains the following:
598
+
599
+ - ``time_captured`` (*datetime*): The time captured.
600
+ - ``time_synced`` (*datetime*): The time synced.
601
+ - ``payload`` (*Dict[str, ValueTypes]*): The latest tabular data captured from the specified data source.
602
+
603
+ For more information, see `Data Client API <https://docs.viam.com/dev/reference/apis/data-client/#getlatesttabulardata>`_.
604
+ """
605
+
606
+ request = GetLatestTabularDataRequest(
607
+ part_id=part_id,
608
+ resource_name=resource_name,
609
+ resource_subtype=resource_api,
610
+ method_name=method_name,
611
+ additional_parameters=dict_to_struct(additional_params) if additional_params is not None else None,
612
+ )
613
+ response: GetLatestTabularDataResponse = await self._data_client.GetLatestTabularData(request, metadata=self._metadata)
614
+ if not response.payload:
615
+ return None
616
+ return response.time_captured.ToDatetime(), response.time_synced.ToDatetime(), struct_to_dict(response.payload)
617
+
618
+ @_alias_param("resource_api", param_alias="resource_subtype")
619
+ async def export_tabular_data(
620
+ self,
621
+ part_id: str,
622
+ resource_name: str,
623
+ resource_api: str,
624
+ method_name: str,
625
+ start_time: Optional[datetime] = None,
626
+ end_time: Optional[datetime] = None,
627
+ additional_params: Optional[Mapping[str, ValueTypes]] = None,
628
+ ) -> List[TabularDataPoint]:
629
+ """Obtain unified tabular data and metadata from the specified data source.
630
+
631
+ ::
632
+
633
+ tabular_data = await data_client.export_tabular_data(
634
+ part_id="<PART-ID>",
635
+ resource_name="<RESOURCE-NAME>",
636
+ resource_api="<RESOURCE-API>",
637
+ method_name="<METHOD-NAME>",
638
+ start_time="<START_TIME>"
639
+ end_time="<END_TIME>"
640
+ additional_params="<ADDITIONAL_PARAMETERS>"
641
+ )
642
+
643
+ print(f"My data: {tabular_data}")
644
+
645
+ Args:
646
+ part_id (str): The ID of the part that owns the data.
647
+ resource_name (str): The name of the requested resource that captured the data.
648
+ resource_api (str): The API of the requested resource that captured the data.
649
+ method_name (str): The data capture method name.
650
+ start_time (datetime): Optional start time for requesting a specific range of data.
651
+ end_time (datetime): Optional end time for requesting a specific range of data.
652
+ additional_params (dict): Optional additional parameters of the resource that captured the data.
653
+
654
+ Returns:
655
+ List[TabularDataPoint]: The unified tabular data and metadata.
656
+
657
+ For more information, see `Data Client API <https://docs.viam.com/dev/reference/apis/data-client/#exporttabulardata>`_.
658
+ """
659
+
660
+ interval = CaptureInterval(start=datetime_to_timestamp(start_time), end=datetime_to_timestamp(end_time))
661
+ request = ExportTabularDataRequest(
662
+ part_id=part_id,
663
+ resource_name=resource_name,
664
+ resource_subtype=resource_api,
665
+ method_name=method_name,
666
+ interval=interval,
667
+ additional_parameters=dict_to_struct(additional_params) if additional_params is not None else None,
668
+ )
669
+ response: List[ExportTabularDataResponse] = await self._data_client.ExportTabularData(request, metadata=self._metadata)
670
+
671
+ return [
672
+ DataClient.TabularDataPoint(
673
+ part_id=resp.part_id,
674
+ resource_name=resp.resource_name,
675
+ resource_api=resp.resource_subtype,
676
+ method_name=resp.method_name,
677
+ time_captured=resp.time_captured.ToDatetime(),
678
+ organization_id=resp.organization_id,
679
+ location_id=resp.location_id,
680
+ robot_name=resp.robot_name,
681
+ robot_id=resp.robot_id,
682
+ part_name=resp.part_name,
683
+ method_parameters=struct_to_dict(resp.method_parameters),
684
+ tags=list(resp.tags),
685
+ payload=struct_to_dict(resp.payload),
686
+ )
687
+ for resp in response
688
+ ]
689
+
690
+ async def binary_data_by_filter(
691
+ self,
692
+ filter: Optional[Filter] = None,
693
+ limit: Optional[int] = None,
694
+ sort_order: Optional[Order.ValueType] = None,
695
+ last: Optional[str] = None,
696
+ include_binary_data: bool = True,
697
+ count_only: bool = False,
698
+ include_internal_data: bool = False,
699
+ dest: Optional[str] = None,
700
+ ) -> Tuple[List[BinaryData], int, str]:
701
+ """Filter and download binary data. The data will be paginated into pages of ``limit`` items, and the pagination ID will be included
702
+ in the returned tuple as ``last``. If a destination is provided, this method saves returned data to that file,
703
+ overwriting any existing file content.
704
+
705
+ ::
706
+
707
+ from viam.utils import create_filter
708
+ from viam.proto.app.data import Filter, TagsFilter, TagsFilterType
709
+
710
+ # Get data captured from camera components
711
+ my_data = []
712
+ last = None
713
+ my_filter = create_filter(component_name="camera-1")
714
+
715
+ while True:
716
+ data, count, last = await data_client.binary_data_by_filter(
717
+ my_filter, limit=1, last=last)
718
+ if not data:
719
+ break
720
+ my_data.extend(data)
721
+
722
+ print(f"My data: {my_data}")
723
+
724
+ # Get untagged data from a dataset
725
+
726
+ my_untagged_data = []
727
+ last = None
728
+ tags_filter = TagsFilter(type=TagsFilterType.TAGS_FILTER_TYPE_UNTAGGED)
729
+ my_filter = Filter(
730
+ dataset_id="66db6fe7d93d1ade24cd1dc3",
731
+ tags_filter=tags_filter
732
+ )
733
+
734
+ while True:
735
+ data, count, last = await data_client.binary_data_by_filter(
736
+ my_filter, last=last, include_binary_data=False)
737
+ if not data:
738
+ break
739
+ my_untagged_data.extend(data)
740
+
741
+ Args:
742
+ filter (~viam.proto.app.data.Filter): Optional, specifies tabular data to retrieve. An empty filter matches all binary data.
743
+ limit (int): The maximum number of entries to include in a page. Defaults to 50 if unspecified.
744
+ sort_order (~viam.proto.app.data.Order): The desired sort order of the data.
745
+ last (str): Optional string indicating the object identifier of the last-returned data.
746
+ This object identifier is returned by calls to :meth:`binary_data_by_filter` as the ``last`` value.
747
+ If provided, the server will return the next data entries after the last object identifier.
748
+ include_binary_data (bool): Boolean specifying whether to actually include the binary file data with each retrieved file.
749
+ Defaults to true (that is, both the files' data and metadata are returned).
750
+ count_only (bool): Whether to return only the total count of entries.
751
+ include_internal_data (bool): Whether to return the internal data. Internal data is used for Viam-specific data ingestion,
752
+ like cloud SLAM. Defaults to ``False``.
753
+ dest (str): Optional filepath for writing retrieved data.
754
+
755
+ Returns:
756
+ Tuple[List[~viam.proto.app.data.BinaryData], int, str]: A tuple containing the following:
757
+
758
+ - ``data`` (*List[* :class:`~viam.proto.app.data.BinaryData` *]*): The binary data.
759
+ - ``count`` (*int*): The count (number of entries).
760
+ - ``last`` (*str*): The last-returned page ID.
761
+
762
+ For more information, see `Data Client API <https://docs.viam.com/dev/reference/apis/data-client/#binarydatabyfilter>`_.
763
+ """
764
+
765
+ data_request = DataRequest(filter=filter)
766
+ if limit:
767
+ data_request.limit = limit
768
+ if sort_order:
769
+ data_request.sort_order = sort_order
770
+ if last:
771
+ data_request.last = last
772
+ request = BinaryDataByFilterRequest(
773
+ data_request=data_request,
774
+ include_binary=include_binary_data,
775
+ count_only=count_only,
776
+ include_internal_data=include_internal_data,
777
+ )
778
+ response: BinaryDataByFilterResponse = await self._data_client.BinaryDataByFilter(request, metadata=self._metadata)
779
+ data = list(response.data)
780
+ if dest:
781
+ try:
782
+ file = open(dest, "w")
783
+ file.write(f"{[str(d) for d in data]}")
784
+ file.flush()
785
+ except Exception as e:
786
+ LOGGER.error(f"Failed to write binary data to file {dest}", exc_info=e)
787
+
788
+ return data, response.count, response.last
789
+
790
+ async def binary_data_by_ids(
791
+ self,
792
+ binary_ids: Union[List[BinaryID], List[str]],
793
+ include_binary_data: bool = True,
794
+ dest: Optional[str] = None,
795
+ ) -> List[BinaryData]:
796
+ """Filter and download binary data.
797
+
798
+ ::
799
+
800
+ binary_metadata, count, last = await data_client.binary_data_by_filter(
801
+ include_binary_data=False
802
+ )
803
+
804
+ my_ids = []
805
+
806
+ for obj in binary_metadata:
807
+ my_ids.append(obj.metadata.binary_data_id)
808
+
809
+ binary_data = await data_client.binary_data_by_ids(my_ids)
810
+
811
+ Args:
812
+ binary_ids (Union[List[~viam.proto.app.data.BinaryID], List[str]]): Binary data ID strings specifying the desired data or
813
+ :class:`BinaryID` objects. Must be non-empty.
814
+ *DEPRECATED:* :class:`BinaryID` *is deprecated and will be removed in a future release. Instead, pass binary data IDs as a
815
+ list of strings.*
816
+ include_binary_data (bool): Boolean specifying whether to actually include the binary file data with each retrieved file.
817
+ Defaults to true (that is, both the files' data and metadata are returned).
818
+ dest (str): Optional filepath for writing retrieved data.
819
+
820
+ Raises:
821
+ GRPCError: If no binary data ID strings or :class:`BinaryID` objects are provided.
822
+
823
+ Returns:
824
+ List[~viam.proto.app.data.BinaryData]: The binary data.
825
+
826
+ For more information, see `Data Client API <https://docs.viam.com/dev/reference/apis/data-client/#binarydatabyids>`_.
827
+ """
828
+ request = BinaryDataByIDsRequest()
829
+ if len(binary_ids) > 0 and isinstance(binary_ids[0], str):
830
+ binary_data_ids = cast(List[str], binary_ids)
831
+ request = BinaryDataByIDsRequest(binary_data_ids=binary_data_ids, include_binary=include_binary_data)
832
+ else:
833
+ bin_ids = cast(List[BinaryID], binary_ids)
834
+ request = BinaryDataByIDsRequest(binary_ids=bin_ids, include_binary=include_binary_data)
835
+ response: BinaryDataByIDsResponse = await self._data_client.BinaryDataByIDs(request, metadata=self._metadata)
836
+ if dest:
837
+ try:
838
+ file = open(dest, "w")
839
+ file.write(f"{response.data}")
840
+ file.flush()
841
+ except Exception as e:
842
+ LOGGER.error(f"Failed to write binary data to file {dest}", exc_info=e)
843
+ return list(response.data)
844
+
845
+ async def delete_tabular_data(self, organization_id: str, delete_older_than_days: int) -> int:
846
+ """Delete tabular data older than a specified number of days.
847
+
848
+ ::
849
+
850
+ tabular_data = await data_client.delete_tabular_data(
851
+ organization_id="<YOUR-ORG-ID>",
852
+ delete_older_than_days=150
853
+ )
854
+
855
+ Args:
856
+ organization_id (str): The ID of the organization to delete the data from.
857
+ To find your organization ID, visit the organization settings page.
858
+ delete_older_than_days (int): Delete data that was captured up to *this many* days ago. For example, a value of
859
+ 10 deletes any data that was captured up to 10 days ago. A value of 0 deletes *all* existing data.
860
+
861
+ Returns:
862
+ int: The number of items deleted.
863
+
864
+ For more information, see `Data Client API <https://docs.viam.com/dev/reference/apis/data-client/#deletetabulardata>`_.
865
+ """
866
+ request = DeleteTabularDataRequest(organization_id=organization_id, delete_older_than_days=delete_older_than_days)
867
+ response: DeleteTabularDataResponse = await self._data_client.DeleteTabularData(request, metadata=self._metadata)
868
+ return response.deleted_count
869
+
870
+ async def delete_tabular_data_by_filter(self, filter: Optional[Filter]) -> int:
871
+ """Deprecated: use :meth:`delete_tabular_data` instead."""
872
+ raise NotImplementedError()
873
+
874
+ async def delete_binary_data_by_filter(self, filter: Optional[Filter]) -> int:
875
+ """Filter and delete binary data.
876
+
877
+ ::
878
+
879
+ from viam.utils import create_filter
880
+
881
+ my_filter = create_filter(component_name="left_motor", organization_ids=["<YOUR-ORG-ID>"])
882
+
883
+ res = await data_client.delete_binary_data_by_filter(my_filter)
884
+
885
+ Args:
886
+ filter (~viam.proto.app.data.Filter): Optional, specifies binary data to delete.
887
+ **CAUTION: Passing an empty** ``Filter`` **deletes all binary data!**
888
+ You must specify an organization ID with ``organization_ids`` when using this option.
889
+ To find your organization ID, visit the organization settings page.
890
+
891
+ Returns:
892
+ int: The number of items deleted.
893
+
894
+ For more information, see `Data Client API <https://docs.viam.com/dev/reference/apis/data-client/#deletebinarydatabyfilter>`_.
895
+ """
896
+ filter = filter if filter else Filter()
897
+ request = DeleteBinaryDataByFilterRequest(filter=filter)
898
+ response: DeleteBinaryDataByFilterResponse = await self._data_client.DeleteBinaryDataByFilter(request, metadata=self._metadata)
899
+ return response.deleted_count
900
+
901
+ async def delete_binary_data_by_ids(self, binary_ids: Union[List[BinaryID], List[str]]) -> int:
902
+ """Filter and delete binary data.
903
+
904
+ ::
905
+
906
+ from viam.proto.app.data import BinaryID
907
+ from viam.utils import create_filter
908
+
909
+ my_filter = create_filter(component_name="camera-1", organization_ids=["<YOUR-ORG-ID>"])
910
+ binary_metadata, count, last = await data_client.binary_data_by_filter(
911
+ filter=my_filter,
912
+ limit=20,
913
+ include_binary_data=False
914
+ )
915
+
916
+ my_ids = []
917
+
918
+ for obj in binary_metadata:
919
+ my_ids.append(
920
+ obj.metadata.binary_data_id
921
+ )
922
+
923
+ binary_data = await data_client.delete_binary_data_by_ids(my_ids)
924
+
925
+ Args:
926
+ binary_ids (Union[List[~viam.proto.app.data.BinaryID], List[str]]): Binary data ID strings specifying the data to be deleted or
927
+ :class:`BinaryID` objects. Must be non-empty.
928
+ *DEPRECATED:* :class:`BinaryID` *is deprecated and will be removed in a future release. Instead, pass binary data IDs as a
929
+ list of strings.*
930
+
931
+ Raises:
932
+ GRPCError: If no binary data ID strings or :class:`BinaryID` objects are provided.
933
+
934
+ Returns:
935
+ int: The number of items deleted.
936
+
937
+ For more information, see `Data Client API <https://docs.viam.com/dev/reference/apis/data-client/#deletebinarydatabyids>`_.
938
+ """
939
+ request = DeleteBinaryDataByIDsRequest()
940
+ if len(binary_ids) > 0 and isinstance(binary_ids[0], str):
941
+ binary_data_ids = cast(List[str], binary_ids)
942
+ request = DeleteBinaryDataByIDsRequest(binary_data_ids=binary_data_ids)
943
+ else:
944
+ bin_ids = cast(List[BinaryID], binary_ids)
945
+ request = DeleteBinaryDataByIDsRequest(binary_ids=bin_ids)
946
+ response: DeleteBinaryDataByIDsResponse = await self._data_client.DeleteBinaryDataByIDs(request, metadata=self._metadata)
947
+ return response.deleted_count
948
+
949
+ async def add_tags_to_binary_data_by_ids(self, tags: List[str], binary_ids: Union[List[BinaryID], List[str]]) -> None:
950
+ """Add tags to binary data.
951
+
952
+ ::
953
+
954
+ from viam.utils import create_filter
955
+
956
+ tags = ["tag1", "tag2"]
957
+
958
+ my_filter = create_filter(component_name="camera-1", organization_ids=["<YOUR-ORG-ID>"])
959
+ binary_metadata, count, last = await data_client.binary_data_by_filter(
960
+ filter=my_filter,
961
+ limit=20,
962
+ include_binary_data=False
963
+ )
964
+
965
+ my_ids = []
966
+
967
+ for obj in binary_metadata:
968
+ my_ids.append(
969
+ obj.metadata.binary_data_id
970
+ )
971
+
972
+ binary_data = await data_client.add_tags_to_binary_data_by_ids(tags, my_ids)
973
+
974
+ Args:
975
+ tags (List[str]): List of tags to add to specified binary data. Must be non-empty.
976
+ binary_ids (Union[List[~viam.proto.app.data.BinaryID], List[str]]): Binary data ID strings specifying the data to be tagged or
977
+ :class:`BinaryID` objects. Must be non-empty.
978
+ *DEPRECATED:* :class:`BinaryID` *is deprecated and will be removed in a future release. Instead, pass binary data IDs as a
979
+ list of strings.*
980
+
981
+ Raises:
982
+ GRPCError: If no binary data ID strings or :class:`BinaryID` objects are provided.
983
+
984
+ For more information, see `Data Client API <https://docs.viam.com/dev/reference/apis/data-client/#addtagstobinarydatabyids>`_.
985
+ """
986
+ request = AddTagsToBinaryDataByIDsRequest()
987
+ if len(binary_ids) > 0 and isinstance(binary_ids[0], str):
988
+ binary_data_ids = cast(List[str], binary_ids)
989
+ request = AddTagsToBinaryDataByIDsRequest(binary_data_ids=binary_data_ids, tags=tags)
990
+ else:
991
+ bin_ids = cast(List[BinaryID], binary_ids)
992
+ request = AddTagsToBinaryDataByIDsRequest(binary_ids=bin_ids, tags=tags)
993
+ await self._data_client.AddTagsToBinaryDataByIDs(request, metadata=self._metadata)
994
+
995
+ async def add_tags_to_binary_data_by_filter(self, tags: List[str], filter: Optional[Filter] = None) -> None:
996
+ """Add tags to binary data.
997
+
998
+ ::
999
+
1000
+ from viam.utils import create_filter
1001
+
1002
+ my_filter = create_filter(component_name="my_camera")
1003
+ tags = ["tag1", "tag2"]
1004
+ await data_client.add_tags_to_binary_data_by_filter(tags, my_filter)
1005
+
1006
+ Args:
1007
+ tags (List[str]): List of tags to add to specified binary data. Must be non-empty.
1008
+ filter (~viam.proto.app.data.Filter): Specifies binary data to tag. If none is provided, tags all data.
1009
+
1010
+ Raises:
1011
+ GRPCError: If no tags are provided.
1012
+
1013
+ For more information, see `Data Client API <https://docs.viam.com/dev/reference/apis/data-client/#addtagstobinarydatabyfilter>`_.
1014
+ """
1015
+ filter = filter if filter else Filter()
1016
+ request = AddTagsToBinaryDataByFilterRequest(filter=filter, tags=tags)
1017
+ await self._data_client.AddTagsToBinaryDataByFilter(request, metadata=self._metadata)
1018
+
1019
+ async def remove_tags_from_binary_data_by_ids(self, tags: List[str], binary_ids: Union[List[BinaryID], List[str]]) -> int:
1020
+ """Remove tags from binary data by IDs.
1021
+
1022
+ ::
1023
+
1024
+ from viam.utils import create_filter
1025
+
1026
+ tags = ["tag1", "tag2"]
1027
+
1028
+ my_filter = create_filter(component_name="camera-1")
1029
+
1030
+ binary_metadata, count, last = await data_client.binary_data_by_filter(
1031
+ filter=my_filter,
1032
+ limit=50,
1033
+ include_binary_data=False
1034
+ )
1035
+
1036
+ my_ids = []
1037
+
1038
+ for obj in binary_metadata:
1039
+ my_ids.append(
1040
+ obj.metadata.binary_data_id
1041
+ )
1042
+
1043
+ binary_data = await data_client.remove_tags_from_binary_data_by_ids(
1044
+ tags, my_ids)
1045
+
1046
+ Args:
1047
+ tags (List[str]): List of tags to remove from specified binary data. Must be non-empty.
1048
+ binary_ids (Union[List[~viam.proto.app.data.BinaryID], List[str]]): Binary data ID strings specifying the data to be untagged
1049
+ or `BinaryID` objects. Must be non-empty.
1050
+ *DEPRECATED:* :class:`BinaryID` *is deprecated and will be removed in a future release. Instead, pass binary data IDs as a
1051
+ list of strings.*
1052
+
1053
+ Raises:
1054
+ GRPCError: If no binary data ID strings, :class:`BinaryID` objects, or tags are provided.
1055
+
1056
+ Returns:
1057
+ int: The number of tags removed.
1058
+
1059
+ For more information, see `Data Client API <https://docs.viam.com/dev/reference/apis/data-client/#removetagsfrombinarydatabyids>`_.
1060
+ """
1061
+ request = RemoveTagsFromBinaryDataByIDsRequest(tags=tags)
1062
+ if len(binary_ids) > 0 and isinstance(binary_ids[0], str):
1063
+ binary_data_ids = cast(List[str], binary_ids)
1064
+ request = RemoveTagsFromBinaryDataByIDsRequest(binary_data_ids=binary_data_ids, tags=tags)
1065
+ else:
1066
+ bin_ids = cast(List[BinaryID], binary_ids)
1067
+ request = RemoveTagsFromBinaryDataByIDsRequest(binary_ids=bin_ids, tags=tags)
1068
+ response: RemoveTagsFromBinaryDataByIDsResponse = await self._data_client.RemoveTagsFromBinaryDataByIDs(
1069
+ request, metadata=self._metadata
1070
+ )
1071
+ return response.deleted_count
1072
+
1073
+ async def remove_tags_from_binary_data_by_filter(self, tags: List[str], filter: Optional[Filter] = None) -> int:
1074
+ """Remove tags from binary data.
1075
+
1076
+ ::
1077
+
1078
+ from viam.utils import create_filter
1079
+
1080
+ my_filter = create_filter(component_name="my_camera")
1081
+ tags = ["tag1", "tag2"]
1082
+ res = await data_client.remove_tags_from_binary_data_by_filter(tags, my_filter)
1083
+
1084
+ Args:
1085
+ tags (List[str]): List of tags to remove from specified binary data.
1086
+ filter (~viam.proto.app.data.Filter): Specifies binary data to untag. If none is provided, removes tags from all data.
1087
+
1088
+ Raises:
1089
+ GRPCError: If no tags are provided.
1090
+
1091
+ Returns:
1092
+ int: The number of tags removed.
1093
+
1094
+ For more information, see `Data Client API <https://docs.viam.com/dev/reference/apis/data-client/#removetagsfrombinarydatabyfilter>`_.
1095
+ """
1096
+ filter = filter if filter else Filter()
1097
+ request = RemoveTagsFromBinaryDataByFilterRequest(filter=filter, tags=tags)
1098
+ response: RemoveTagsFromBinaryDataByFilterResponse = await self._data_client.RemoveTagsFromBinaryDataByFilter(
1099
+ request, metadata=self._metadata
1100
+ )
1101
+ return response.deleted_count
1102
+
1103
+ async def tags_by_filter(self, filter: Optional[Filter] = None) -> List[str]:
1104
+ """Get a list of tags using a filter.
1105
+
1106
+ ::
1107
+
1108
+ from viam.utils import create_filter
1109
+
1110
+ my_filter = create_filter(component_name="my_camera")
1111
+ tags = await data_client.tags_by_filter(my_filter)
1112
+
1113
+ Args:
1114
+ filter (~viam.proto.app.data.Filter): Specifies subset ofdata to retrieve tags from. If none is provided, returns all tags.
1115
+
1116
+ Returns:
1117
+ List[str]: The list of tags.
1118
+
1119
+ For more information, see `Data Client API <https://docs.viam.com/dev/reference/apis/data-client/#tagsbyfilter>`_.
1120
+ """
1121
+ filter = filter if filter else Filter()
1122
+ request = TagsByFilterRequest(filter=filter)
1123
+ response: TagsByFilterResponse = await self._data_client.TagsByFilter(request, metadata=self._metadata)
1124
+ return list(response.tags)
1125
+
1126
+ async def add_bounding_box_to_image_by_id(
1127
+ self,
1128
+ binary_id: Union[BinaryID, str],
1129
+ label: str,
1130
+ x_min_normalized: float,
1131
+ y_min_normalized: float,
1132
+ x_max_normalized: float,
1133
+ y_max_normalized: float,
1134
+ ) -> str:
1135
+ """Add a bounding box to an image.
1136
+
1137
+ ::
1138
+
1139
+ bbox_id = await data_client.add_bounding_box_to_image_by_id(
1140
+ binary_id="<YOUR-BINARY-DATA-ID>",
1141
+ label="label",
1142
+ x_min_normalized=0,
1143
+ y_min_normalized=.1,
1144
+ x_max_normalized=.2,
1145
+ y_max_normalized=.3
1146
+ )
1147
+
1148
+ print(bbox_id)
1149
+
1150
+ Args:
1151
+ binary_id (Union[~viam.proto.app.data.BinaryID, str]): The binary data ID or :class:`BinaryID` of the image to add the bounding
1152
+ box to. *DEPRECATED:* :class:`BinaryID` *is deprecated and will be removed in a future release. Instead, pass binary data IDs as a
1153
+ list of strings.*
1154
+ label (str): A label for the bounding box.
1155
+ x_min_normalized (float): Min X value of the bounding box normalized from 0 to 1.
1156
+ y_min_normalized (float): Min Y value of the bounding box normalized from 0 to 1.
1157
+ x_max_normalized (float): Max X value of the bounding box normalized from 0 to 1.
1158
+ y_max_normalized (float): Max Y value of the bounding box normalized from 0 to 1.
1159
+
1160
+ Raises:
1161
+ GRPCError: If the X or Y values are outside of the [0, 1] range.
1162
+
1163
+ Returns:
1164
+ str: The bounding box ID.
1165
+
1166
+ For more information, see `Data Client API <https://docs.viam.com/dev/reference/apis/data-client/#addboundingboxtoimagebyid>`_.
1167
+ """
1168
+ request = AddBoundingBoxToImageByIDRequest()
1169
+ if isinstance(binary_id, str):
1170
+ request = AddBoundingBoxToImageByIDRequest(
1171
+ binary_data_id=binary_id,
1172
+ label=label,
1173
+ x_max_normalized=x_max_normalized,
1174
+ x_min_normalized=x_min_normalized,
1175
+ y_max_normalized=y_max_normalized,
1176
+ y_min_normalized=y_min_normalized,
1177
+ )
1178
+ else:
1179
+ request = AddBoundingBoxToImageByIDRequest(
1180
+ binary_id=binary_id,
1181
+ label=label,
1182
+ x_max_normalized=x_max_normalized,
1183
+ x_min_normalized=x_min_normalized,
1184
+ y_max_normalized=y_max_normalized,
1185
+ y_min_normalized=y_min_normalized,
1186
+ )
1187
+ response: AddBoundingBoxToImageByIDResponse = await self._data_client.AddBoundingBoxToImageByID(request, metadata=self._metadata)
1188
+ return response.bbox_id
1189
+
1190
+ async def remove_bounding_box_from_image_by_id(self, bbox_id: str, binary_id: Union[BinaryID, str]) -> None:
1191
+ """Removes a bounding box from an image.
1192
+
1193
+ ::
1194
+
1195
+ await data_client.remove_bounding_box_from_image_by_id(
1196
+ binary_id="<YOUR-BINARY-DATA-ID>",
1197
+ bbox_id="your-bounding-box-id-to-delete"
1198
+ )
1199
+
1200
+ Args:
1201
+ bbox_id (str): The ID of the bounding box to remove.
1202
+ binary_id (Union[~viam.proto.app.data.BinaryID, str]): The binary data ID or :class:`BinaryID` of the image to remove the
1203
+ bounding box from.
1204
+ *DEPRECATED:* :class:`BinaryID` *is deprecated and will be removed in a future release. Instead, pass binary data IDs as a
1205
+ list of strings.*
1206
+
1207
+ For more information, see `Data Client API <https://docs.viam.com/dev/reference/apis/data-client/#removeboundingboxfromimagebyid>`_.
1208
+ """
1209
+ request = RemoveBoundingBoxFromImageByIDRequest()
1210
+ if isinstance(binary_id, str):
1211
+ request = RemoveBoundingBoxFromImageByIDRequest(binary_data_id=binary_id, bbox_id=bbox_id)
1212
+ else:
1213
+ request = RemoveBoundingBoxFromImageByIDRequest(binary_id=binary_id, bbox_id=bbox_id)
1214
+
1215
+ await self._data_client.RemoveBoundingBoxFromImageByID(request, metadata=self._metadata)
1216
+
1217
+ async def bounding_box_labels_by_filter(self, filter: Optional[Filter] = None) -> List[str]:
1218
+ """Get a list of bounding box labels using a `Filter`.
1219
+
1220
+ ::
1221
+
1222
+ from viam.utils import create_filter
1223
+
1224
+ my_filter = create_filter(component_name="my_camera")
1225
+ bounding_box_labels = await data_client.bounding_box_labels_by_filter(
1226
+ my_filter)
1227
+
1228
+ print(bounding_box_labels)
1229
+
1230
+ Args:
1231
+ filter (~viam.proto.app.data.Filter): Specifies data to retrieve bounding box labels from. If none is provided, returns labels
1232
+ from all data.
1233
+
1234
+ Returns:
1235
+ List[str]: The list of bounding box labels.
1236
+
1237
+ For more information, see `Data Client API <https://docs.viam.com/dev/reference/apis/data-client/#boundingboxlabelsbyfilter>`_.
1238
+ """
1239
+ filter = filter if filter else Filter()
1240
+ request = BoundingBoxLabelsByFilterRequest(filter=filter)
1241
+ response: BoundingBoxLabelsByFilterResponse = await self._data_client.BoundingBoxLabelsByFilter(request, metadata=self._metadata)
1242
+ return list(response.labels)
1243
+
1244
+ async def get_database_connection(self, organization_id: str) -> str:
1245
+ """Get a connection to access a MongoDB Atlas Data federation instance.
1246
+
1247
+ ::
1248
+
1249
+ hostname = await data_client.get_database_connection(organization_id="<YOUR-ORG-ID>")
1250
+
1251
+ Args:
1252
+ organization_id (str): The ID of the organization you'd like to connect to.
1253
+ To find your organization ID, visit the organization settings page.
1254
+
1255
+ Returns:
1256
+ str: The hostname of the federated database.
1257
+
1258
+ For more information, see `Data Client API <https://docs.viam.com/dev/reference/apis/data-client/#getdatabaseconnection>`_.
1259
+ """
1260
+ request = GetDatabaseConnectionRequest(organization_id=organization_id)
1261
+ response: GetDatabaseConnectionResponse = await self._data_client.GetDatabaseConnection(request, metadata=self._metadata)
1262
+ return response.hostname
1263
+
1264
+ async def configure_database_user(self, organization_id: str, password: str) -> None:
1265
+ """Configure a database user for the Viam organization's MongoDB Atlas Data Federation instance. It can also be used to reset the
1266
+ password of the existing database user.
1267
+
1268
+ ::
1269
+
1270
+ await data_client.configure_database_user(
1271
+ organization_id="<YOUR-ORG-ID>",
1272
+ password="Your_Password@1234"
1273
+ )
1274
+
1275
+ Args:
1276
+ organization_id (str): The ID of the organization you'd like to configure a database user for.
1277
+ To find your organization ID, visit the organization settings page.
1278
+ password (str): The password of the user.
1279
+
1280
+ For more information, see `Data Client API <https://docs.viam.com/dev/reference/apis/data-client/#configuredatabaseuser>`_.
1281
+ """
1282
+ request = ConfigureDatabaseUserRequest(organization_id=organization_id, password=password)
1283
+ await self._data_client.ConfigureDatabaseUser(request, metadata=self._metadata)
1284
+
1285
+ async def create_dataset(self, name: str, organization_id: str) -> str:
1286
+ """Create a new dataset.
1287
+
1288
+ ::
1289
+
1290
+ dataset_id = await data_client.create_dataset(
1291
+ name="<DATASET-NAME>",
1292
+ organization_id="<YOUR-ORG-ID>"
1293
+ )
1294
+ print(dataset_id)
1295
+
1296
+ Args:
1297
+ name (str): The name of the dataset being created.
1298
+ organization_id (str): The ID of the organization where the dataset is being created.
1299
+ To find your organization ID, visit the organization settings page.
1300
+
1301
+ Returns:
1302
+ str: The dataset ID of the created dataset.
1303
+
1304
+ For more information, see `Data Client API <https://docs.viam.com/dev/reference/apis/data-client/#createdataset>`_.
1305
+ """
1306
+ request = CreateDatasetRequest(name=name, organization_id=organization_id)
1307
+ response: CreateDatasetResponse = await self._dataset_client.CreateDataset(request, metadata=self._metadata)
1308
+ return response.id
1309
+
1310
+ async def merge_datasets(self, name: str, organization_id: str, dataset_ids: List[str]) -> str:
1311
+ """Merge multiple datasets into a new dataset.
1312
+
1313
+ ::
1314
+
1315
+ dataset_id = await data_client.merge_datasets(
1316
+ name="<DATASET-NAME>",
1317
+ organization_id="<YOUR-ORG-ID>",
1318
+ dataset_ids=["<YOUR-DATASET-ID-1>", "<YOUR-DATASET-ID-2>"]
1319
+ )
1320
+ print(dataset_id)
1321
+
1322
+ Args:
1323
+ name (str): The name of the dataset being created.
1324
+ organization_id (str): The ID of the organization where the dataset is being created.
1325
+ To find your organization ID, visit the organization settings page.
1326
+ dataset_ids (List[str]): The IDs of the datasets that you would like to merge.
1327
+ Returns:
1328
+ str: The dataset ID of the created dataset.
1329
+
1330
+ For more information, see `Data Client API <https://docs.viam.com/dev/reference/apis/data-client/#mergedatasets>`_.
1331
+ """
1332
+ request = MergeDatasetsRequest(name=name, organization_id=organization_id, dataset_ids=dataset_ids)
1333
+ response: MergeDatasetsResponse = await self._dataset_client.MergeDatasets(request, metadata=self._metadata)
1334
+ return response.dataset_id
1335
+
1336
+ async def list_dataset_by_ids(self, ids: List[str]) -> Sequence[Dataset]:
1337
+ """Get a list of datasets using their IDs.
1338
+
1339
+ ::
1340
+
1341
+ datasets = await data_client.list_dataset_by_ids(
1342
+ ids=["<YOUR-DATASET-ID-1>, <YOUR-DATASET-ID-2>"]
1343
+ )
1344
+ print(datasets)
1345
+
1346
+ Args:
1347
+ ids (List[str]): The IDs of the datasets that you would like to retrieve information about. To retrieve a dataset ID:
1348
+
1349
+ - Navigate to the **DATASETS** tab of the **DATA** page.
1350
+ - Click on the dataset.
1351
+ - Click the **...** menu.
1352
+ - Select **Copy dataset ID**.
1353
+
1354
+ Returns:
1355
+ Sequence[Dataset]: The list of datasets.
1356
+
1357
+ For more information, see `Data Client API <https://docs.viam.com/dev/reference/apis/data-client/#listdatasetsbyids>`_.
1358
+ """
1359
+ request = ListDatasetsByIDsRequest(ids=ids)
1360
+ response: ListDatasetsByIDsResponse = await self._dataset_client.ListDatasetsByIDs(request, metadata=self._metadata)
1361
+
1362
+ return response.datasets
1363
+
1364
+ async def list_datasets_by_organization_id(self, organization_id: str) -> Sequence[Dataset]:
1365
+ """Get the datasets in an organization.
1366
+
1367
+ ::
1368
+
1369
+ datasets = await data_client.list_datasets_by_organization_id(
1370
+ organization_id="<YOUR-ORG-ID>"
1371
+ )
1372
+ print(datasets)
1373
+
1374
+ Args:
1375
+ organization_id (str): The ID of the organization you'd like to retrieve datasets from.
1376
+ To find your organization ID, visit the organization settings page.
1377
+
1378
+ Returns:
1379
+ Sequence[Dataset]: The list of datasets in the organization.
1380
+
1381
+ For more information, see `Data Client API <https://docs.viam.com/dev/reference/apis/data-client/#listdatasetsbyorganizationid>`_.
1382
+ """
1383
+ request = ListDatasetsByOrganizationIDRequest(organization_id=organization_id)
1384
+ response: ListDatasetsByOrganizationIDResponse = await self._dataset_client.ListDatasetsByOrganizationID(
1385
+ request, metadata=self._metadata
1386
+ )
1387
+
1388
+ return response.datasets
1389
+
1390
+ async def rename_dataset(self, id: str, name: str) -> None:
1391
+ """Rename a dataset specified by the dataset ID.
1392
+
1393
+ ::
1394
+
1395
+ await data_client.rename_dataset(
1396
+ id="<YOUR-DATASET-ID>",
1397
+ name="MyDataset"
1398
+ )
1399
+
1400
+ Args:
1401
+ id (str): The ID of the dataset. To retrieve the dataset ID:
1402
+
1403
+ - Navigate to the **DATASETS** tab of the **DATA** page.
1404
+ - Click on the dataset.
1405
+ - Click the **...** menu.
1406
+ - Select **Copy dataset ID**.
1407
+ name (str): The new name of the dataset.
1408
+
1409
+ For more information, see `Data Client API <https://docs.viam.com/dev/reference/apis/data-client/#renamedataset>`_.
1410
+ """
1411
+ request = RenameDatasetRequest(id=id, name=name)
1412
+ await self._dataset_client.RenameDataset(request, metadata=self._metadata)
1413
+
1414
+ async def delete_dataset(self, id: str) -> None:
1415
+ """Delete a dataset.
1416
+
1417
+ ::
1418
+
1419
+ await data_client.delete_dataset(
1420
+ id="<YOUR-DATASET-ID>"
1421
+ )
1422
+
1423
+ Args:
1424
+ id (str): The ID of the dataset. To retrieve the dataset ID:
1425
+
1426
+ - Navigate to the **DATASETS** tab of the **DATA** page.
1427
+ - Click on the dataset.
1428
+ - Click the **...** menu.
1429
+ - Select **Copy dataset ID**.
1430
+
1431
+ For more information, see `Data Client API <https://docs.viam.com/dev/reference/apis/data-client/#deletedataset>`_.
1432
+ """
1433
+ request = DeleteDatasetRequest(id=id)
1434
+ await self._dataset_client.DeleteDataset(request, metadata=self._metadata)
1435
+
1436
+ async def add_binary_data_to_dataset_by_ids(self, binary_ids: Union[List[BinaryID], List[str]], dataset_id: str) -> None:
1437
+ """Add the BinaryData to the provided dataset.
1438
+
1439
+ This BinaryData will be tagged with the VIAM_DATASET_{id} label.
1440
+
1441
+ ::
1442
+
1443
+ binary_metadata, count, last = await data_client.binary_data_by_filter(
1444
+ include_binary_data=False
1445
+ )
1446
+
1447
+ my_binary_data_ids = []
1448
+
1449
+ for obj in binary_metadata:
1450
+ my_binary_data_ids.append(
1451
+ obj.metadata.binary_data_id
1452
+ )
1453
+
1454
+ await data_client.add_binary_data_to_dataset_by_ids(
1455
+ binary_ids=my_binary_data_ids,
1456
+ dataset_id="abcd-1234xyz-8765z-123abc"
1457
+ )
1458
+
1459
+ Args:
1460
+ binary_ids (List[~viam.proto.app.data.BinaryID]): Unique identifiers for binary data to add to the dataset. To retrieve these IDs,
1461
+ navigate to the DATA page, click on an image, and copy its Binary Data ID from the details tab.
1462
+ dataset_id (str): The ID of the dataset to be added to. To retrieve the dataset ID:
1463
+
1464
+ - Navigate to the **DATASETS** tab of the **DATA** page.
1465
+ - Click on the dataset.
1466
+ - Click the **...** menu.
1467
+ - Select **Copy dataset ID**.
1468
+
1469
+ For more information, see `Data Client API <https://docs.viam.com/dev/reference/apis/data-client/#addbinarydatatodatasetbyids>`_.
1470
+ """
1471
+ request = AddBinaryDataToDatasetByIDsRequest()
1472
+ if len(binary_ids) > 0 and isinstance(binary_ids[0], str):
1473
+ binary_data_ids = cast(List[str], binary_ids)
1474
+ request = AddBinaryDataToDatasetByIDsRequest(binary_data_ids=binary_data_ids, dataset_id=dataset_id)
1475
+ else:
1476
+ bin_ids = cast(List[BinaryID], binary_ids)
1477
+ request = AddBinaryDataToDatasetByIDsRequest(binary_ids=bin_ids, dataset_id=dataset_id)
1478
+ await self._data_client.AddBinaryDataToDatasetByIDs(request, metadata=self._metadata)
1479
+
1480
+ async def remove_binary_data_from_dataset_by_ids(self, binary_ids: Union[List[BinaryID], List[str]], dataset_id: str) -> None:
1481
+ """Remove the BinaryData from the provided dataset.
1482
+
1483
+ This BinaryData will lose the VIAM_DATASET_{id} tag.
1484
+
1485
+ ::
1486
+
1487
+ binary_metadata, count, last = await data_client.binary_data_by_filter(
1488
+ include_binary_data=False
1489
+ )
1490
+
1491
+ my_binary_data_ids = []
1492
+
1493
+ for obj in binary_metadata:
1494
+ my_binary_data_ids.append(
1495
+ obj.metadata.binary_data_id
1496
+ )
1497
+
1498
+ await data_client.remove_binary_data_from_dataset_by_ids(
1499
+ binary_ids=my_binary_data_ids,
1500
+ dataset_id="abcd-1234xyz-8765z-123abc"
1501
+ )
1502
+
1503
+ Args:
1504
+ binary_ids (Union[List[~viam.proto.app.data.BinaryID], List[str]]): Unique identifiers for the binary data to remove from the dataset. To retrieve these IDs,
1505
+ navigate to the DATA page, click on an image and copy its Binary Data ID from the details tab.
1506
+ *DEPRECATED:* :class:`BinaryID` *is deprecated and will be removed in a future release. Instead, pass binary data IDs as a
1507
+ list of strings.*
1508
+ dataset_id (str): The ID of the dataset to be removed from. To retrieve the dataset ID:
1509
+
1510
+ - Navigate to the **DATASETS** tab of the **DATA** page.
1511
+ - Click on the dataset.
1512
+ - Click the **...** menu.
1513
+ - Select **Copy dataset ID**.
1514
+
1515
+ For more information, see `Data Client API <https://docs.viam.com/dev/reference/apis/data-client/#removebinarydatafromdatasetbyids>`_.
1516
+ """
1517
+ request = RemoveBinaryDataFromDatasetByIDsRequest()
1518
+ if len(binary_ids) > 0 and isinstance(binary_ids[0], str):
1519
+ binary_data_ids = cast(List[str], binary_ids)
1520
+ request = RemoveBinaryDataFromDatasetByIDsRequest(binary_data_ids=binary_data_ids, dataset_id=dataset_id)
1521
+ else:
1522
+ bin_ids = cast(List[BinaryID], binary_ids)
1523
+ request = RemoveBinaryDataFromDatasetByIDsRequest(binary_ids=bin_ids, dataset_id=dataset_id)
1524
+ await self._data_client.RemoveBinaryDataFromDatasetByIDs(request, metadata=self._metadata)
1525
+
1526
+ async def binary_data_capture_upload(
1527
+ self,
1528
+ binary_data: bytes,
1529
+ part_id: str,
1530
+ component_type: str,
1531
+ component_name: str,
1532
+ method_name: str,
1533
+ file_extension: str,
1534
+ method_parameters: Optional[Mapping[str, Any]] = None,
1535
+ tags: Optional[List[str]] = None,
1536
+ dataset_ids: Optional[List[str]] = None,
1537
+ data_request_times: Optional[Tuple[datetime, datetime]] = None,
1538
+ ) -> str:
1539
+ """Upload binary sensor data.
1540
+
1541
+ Upload binary data collected on a robot through a specific component (for example, a motor), along with the relevant metadata.
1542
+ Binary data can be found on the **DATA** page.
1543
+
1544
+ ::
1545
+
1546
+ time_requested = datetime(2023, 6, 5, 11)
1547
+ time_received = datetime(2023, 6, 5, 11, 0, 3)
1548
+
1549
+ file_id = await data_client.binary_data_capture_upload(
1550
+ part_id="INSERT YOUR PART ID",
1551
+ component_type='camera',
1552
+ component_name='my_camera',
1553
+ method_name='GetImages',
1554
+ method_parameters=None,
1555
+ tags=["tag_1", "tag_2"],
1556
+ data_request_times=[time_requested, time_received],
1557
+ file_extension=".jpg",
1558
+ binary_data=b"Encoded image bytes",
1559
+ dataset_ids=["dataset_1", "dataset_2"]
1560
+ )
1561
+
1562
+ Args:
1563
+ binary_data (bytes): The data to be uploaded, represented in bytes.
1564
+ part_id (str): Part ID of the component used to capture the data.
1565
+ component_type (str): Type of the component used to capture the data (for example, "movement_sensor").
1566
+ component_name (str): Name of the component used to capture the data.
1567
+ method_name (str): Name of the method used to capture the data.
1568
+ file_extension (str): The file extension of binary data, *including the period*, for example ``.jpg``, ``.png``, ``.pcd``.
1569
+ The backend routes the binary to its corresponding mime type based on this extension. Files with a ``.jpeg``, ``.jpg``,
1570
+ or ``.png`` extension will appear in the **Images** tab.
1571
+ method_parameters (Optional[Mapping[str, Any]]): Optional dictionary of method parameters. No longer in active use.
1572
+ tags (Optional[List[str]]): Optional list of tags to allow for tag-based data filtering when retrieving data.
1573
+ dataset_ids (Optional[List[str]]): Optional list of datasets to add the data to.
1574
+ data_request_times (Optional[Tuple[datetime.datetime, datetime.datetime]]): Optional tuple containing datetime objects
1575
+ denoting the times this data was requested ``[0]`` by the robot and received ``[1]`` from the appropriate sensor.
1576
+
1577
+ Raises:
1578
+ GRPCError: If an invalid part ID is passed.
1579
+
1580
+ Returns:
1581
+ str: The binary data ID of the uploaded data.
1582
+
1583
+ For more information, see `Data Client API <https://docs.viam.com/dev/reference/apis/data-client/#binarydatacaptureupload>`_.
1584
+ """
1585
+ sensor_contents = SensorData(
1586
+ metadata=(
1587
+ SensorMetadata(
1588
+ time_requested=datetime_to_timestamp(data_request_times[0]) if data_request_times else None,
1589
+ time_received=datetime_to_timestamp(data_request_times[1]) if data_request_times else None,
1590
+ )
1591
+ if data_request_times
1592
+ else None
1593
+ ),
1594
+ struct=None, # Used for tabular data.
1595
+ binary=binary_data,
1596
+ )
1597
+ metadata = UploadMetadata(
1598
+ part_id=part_id,
1599
+ component_type=component_type,
1600
+ component_name=component_name,
1601
+ method_name=method_name,
1602
+ type=DataType.DATA_TYPE_BINARY_SENSOR,
1603
+ method_parameters=method_parameters,
1604
+ tags=tags,
1605
+ dataset_ids=dataset_ids,
1606
+ )
1607
+ if file_extension:
1608
+ metadata.file_extension = file_extension if file_extension[0] == "." else f".{file_extension}"
1609
+ response = await self._data_capture_upload(metadata=metadata, sensor_contents=[sensor_contents])
1610
+ return response.binary_data_id
1611
+
1612
+ async def tabular_data_capture_upload(
1613
+ self,
1614
+ tabular_data: List[Mapping[str, Any]],
1615
+ part_id: str,
1616
+ component_type: str,
1617
+ component_name: str,
1618
+ method_name: str,
1619
+ data_request_times: List[Tuple[datetime, datetime]],
1620
+ method_parameters: Optional[Mapping[str, Any]] = None,
1621
+ tags: Optional[List[str]] = None,
1622
+ ) -> str:
1623
+ """Upload tabular sensor data.
1624
+
1625
+ Upload tabular data collected on a robot through a specific component (for example, a motor), along with the relevant metadata.
1626
+ Tabular data can be found under the **Sensors** tab of the **DATA** page.
1627
+
1628
+ ::
1629
+
1630
+ from datetime import datetime
1631
+
1632
+ time_requested = datetime(2023, 6, 5, 11)
1633
+ time_received = datetime(2023, 6, 5, 11, 0, 3)
1634
+ file_id = await data_client.tabular_data_capture_upload(
1635
+ part_id="INSERT YOUR PART ID",
1636
+ component_type='rdk:component:movement_sensor',
1637
+ component_name='my_movement_sensor',
1638
+ method_name='Readings',
1639
+ tags=["sensor_data"],
1640
+ data_request_times=[(time_requested, time_received)],
1641
+ tabular_data=[{
1642
+ 'readings': {
1643
+ 'linear_velocity': {'x': 0.5, 'y': 0.0, 'z': 0.0},
1644
+ 'angular_velocity': {'x': 0.0, 'y': 0.0, 'z': 0.1}
1645
+ }
1646
+ }]
1647
+ )
1648
+
1649
+ Args:
1650
+ tabular_data (List[Mapping[str, Any]]): List of the data to be uploaded, represented tabularly as a collection of dictionaries.
1651
+ Must include the key ``readings`` for sensors.
1652
+ part_id (str): Part ID of the component used to capture the data.
1653
+ component_type (str): Type of the component used to capture the data (for example, ``rdk:component:movement_sensor``).
1654
+ component_name (str): Name of the component used to capture the data.
1655
+ method_name (str): Name of the method used to capture the data.
1656
+ data_request_times (List[Tuple[datetime.datetime, datetime.datetime]]): List of tuples, each containing ``datetime`` objects
1657
+ denoting the times this data was requested ``[0]`` by the robot and received ``[1]`` from the appropriate sensor.
1658
+ Pass a list of tabular data and timestamps with length ``n > 1`` to upload ``n`` datapoints, all with the same metadata.
1659
+ method_parameters (Optional[Mapping[str, Any]]): Optional dictionary of method parameters. No longer in active use.
1660
+ tags (Optional[List[str]]): Optional list of tags to allow for tag-based data filtering when retrieving data.
1661
+
1662
+ Raises:
1663
+ GRPCError: If an invalid part ID is passed.
1664
+ ValueError: If the provided list of `Timestamp` objects has a length that does not match the length of the list of tabular
1665
+ data.
1666
+
1667
+ Returns:
1668
+ str: The file ID of the uploaded data.
1669
+
1670
+ For more information, see `Data Client API <https://docs.viam.com/dev/reference/apis/data-client/#tabulardatacaptureupload>`_.
1671
+ """
1672
+ sensor_contents = []
1673
+ if len(data_request_times) != len(tabular_data):
1674
+ raise ValueError("data_request_times and tabular_data lengths must be equal.")
1675
+
1676
+ for idx, tab in enumerate(tabular_data):
1677
+ s = Struct()
1678
+ s.update(tab)
1679
+ sensor_contents.append(
1680
+ SensorData(
1681
+ metadata=(
1682
+ SensorMetadata(
1683
+ time_requested=datetime_to_timestamp(data_request_times[idx][0]) if data_request_times else None,
1684
+ time_received=datetime_to_timestamp(data_request_times[idx][1]) if data_request_times else None,
1685
+ )
1686
+ if data_request_times[idx]
1687
+ else None
1688
+ )
1689
+ if data_request_times
1690
+ else None,
1691
+ struct=s,
1692
+ )
1693
+ )
1694
+
1695
+ metadata = UploadMetadata(
1696
+ part_id=part_id,
1697
+ component_type=component_type,
1698
+ component_name=component_name,
1699
+ method_name=method_name,
1700
+ type=DataType.DATA_TYPE_TABULAR_SENSOR,
1701
+ method_parameters=method_parameters,
1702
+ tags=tags,
1703
+ )
1704
+ response = await self._data_capture_upload(metadata=metadata, sensor_contents=sensor_contents)
1705
+ return response.file_id
1706
+
1707
+ async def _data_capture_upload(self, metadata: UploadMetadata, sensor_contents: List[SensorData]) -> DataCaptureUploadResponse:
1708
+ request = DataCaptureUploadRequest(metadata=metadata, sensor_contents=sensor_contents)
1709
+ response: DataCaptureUploadResponse = await self._data_sync_client.DataCaptureUpload(request, metadata=self._metadata)
1710
+ return response
1711
+
1712
+ async def streaming_data_capture_upload(
1713
+ self,
1714
+ data: bytes,
1715
+ part_id: str,
1716
+ file_ext: str,
1717
+ component_type: Optional[str] = None,
1718
+ component_name: Optional[str] = None,
1719
+ method_name: Optional[str] = None,
1720
+ method_parameters: Optional[Mapping[str, Any]] = None,
1721
+ data_request_times: Optional[Tuple[datetime, datetime]] = None,
1722
+ tags: Optional[List[str]] = None,
1723
+ dataset_ids: Optional[List[str]] = None,
1724
+ ) -> str:
1725
+ """Uploads the metadata and contents of streaming binary data.
1726
+
1727
+ ::
1728
+
1729
+ time_requested = datetime(2023, 6, 5, 11)
1730
+ time_received = datetime(2023, 6, 5, 11, 0, 3)
1731
+
1732
+ file_id = await data_client.streaming_data_capture_upload(
1733
+ data="byte-data-to-upload",
1734
+ part_id="INSERT YOUR PART ID",
1735
+ file_ext="png",
1736
+ component_type='motor',
1737
+ component_name='left_motor',
1738
+ method_name='IsPowered',
1739
+ data_request_times=[time_requested, time_received],
1740
+ tags=["tag_1", "tag_2"],
1741
+ dataset_ids=["dataset_1", "dataset_2"]
1742
+ )
1743
+
1744
+ Args:
1745
+ data (bytes): The data to be uploaded.
1746
+ part_id (str): Part ID of the resource associated with the file.
1747
+ file_ext (str): File extension type for the data. required for determining MIME type.
1748
+ component_type (Optional[str]): Optional type of the component associated with the file (for example, "movement_sensor").
1749
+ component_name (Optional[str]): Optional name of the component associated with the file.
1750
+ method_name (Optional[str]): Optional name of the method associated with the file.
1751
+ method_parameters (Optional[str]): Optional dictionary of the method parameters. No longer in active use.
1752
+ data_request_times (Optional[Tuple[datetime.datetime, datetime.datetime]]): Optional tuple containing datetime objects
1753
+ denoting the times this data was requested ``[0]`` by the robot and received ``[1]`` from the appropriate sensor.
1754
+ tags (Optional[List[str]]): Optional list of tags to allow for tag-based filtering when retrieving data.
1755
+ dataset_ids (Optional[List[str]]): Optional list of datasets to add the data to.
1756
+
1757
+ Raises:
1758
+ GRPCError: If an invalid part ID is passed.
1759
+
1760
+ Returns:
1761
+ str: The binary data ID of the uploaded data.
1762
+
1763
+ For more information, see `Data Client API <https://docs.viam.com/dev/reference/apis/data-client/#streamingdatacaptureupload>`_.
1764
+ """
1765
+
1766
+ upload_metadata = UploadMetadata(
1767
+ part_id=part_id,
1768
+ component_type=component_type if component_type else "",
1769
+ component_name=component_name if component_name else "",
1770
+ method_name=method_name if method_name else "",
1771
+ method_parameters=method_parameters,
1772
+ type=DataType.DATA_TYPE_BINARY_SENSOR,
1773
+ file_extension=file_ext if file_ext[0] == "." else f".{file_ext}",
1774
+ tags=tags,
1775
+ dataset_ids=dataset_ids,
1776
+ )
1777
+ sensor_metadata = SensorMetadata(
1778
+ time_requested=datetime_to_timestamp(data_request_times[0]) if data_request_times else None,
1779
+ time_received=datetime_to_timestamp(data_request_times[1]) if data_request_times else None,
1780
+ )
1781
+ metadata = DataCaptureUploadMetadata(upload_metadata=upload_metadata, sensor_metadata=sensor_metadata)
1782
+ request_metadata = StreamingDataCaptureUploadRequest(metadata=metadata)
1783
+ stream: Stream[StreamingDataCaptureUploadRequest, StreamingDataCaptureUploadResponse]
1784
+ async with self._data_sync_client.StreamingDataCaptureUpload.open(metadata=self._metadata) as stream:
1785
+ await stream.send_message(request_metadata)
1786
+ await stream.send_message(StreamingDataCaptureUploadRequest(data=data), end=True)
1787
+ response = await stream.recv_message()
1788
+ if not response:
1789
+ await stream.recv_trailing_metadata() # causes us to throw appropriate gRPC error
1790
+ raise TypeError("Response cannot be empty")
1791
+ return response.binary_data_id
1792
+
1793
+ async def file_upload(
1794
+ self,
1795
+ part_id: str,
1796
+ data: bytes,
1797
+ component_type: Optional[str] = None,
1798
+ component_name: Optional[str] = None,
1799
+ method_name: Optional[str] = None,
1800
+ file_name: Optional[str] = None,
1801
+ method_parameters: Optional[Mapping[str, Any]] = None,
1802
+ file_extension: Optional[str] = None,
1803
+ tags: Optional[List[str]] = None,
1804
+ dataset_ids: Optional[List[str]] = None,
1805
+ ) -> str:
1806
+ """Upload arbitrary file data.
1807
+
1808
+ Upload file data that may be stored on a robot along with the relevant metadata. File data can be found in the
1809
+ **Files** tab of the **DATA** page.
1810
+
1811
+ ::
1812
+
1813
+ file_id = await data_client.file_upload(
1814
+ data=b"Encoded image bytes",
1815
+ part_id="INSERT YOUR PART ID",
1816
+ tags=["tag_1", "tag_2"],
1817
+ file_name="your-file",
1818
+ file_extension=".txt",
1819
+ dataset_ids=["dataset_1", "dataset_2"]
1820
+ )
1821
+
1822
+ Args:
1823
+ part_id (str): Part ID of the resource associated with the file.
1824
+ data (bytes): Bytes representing file data to upload.
1825
+ component_type (Optional[str]): Optional type of the component associated with the file (for example, "movement_sensor").
1826
+ component_name (Optional[str]): Optional name of the component associated with the file.
1827
+ method_name (Optional[str]): Optional name of the method associated with the file.
1828
+ file_name (Optional[str]): Optional name of the file. The empty string ``""`` will be assigned as the file name if one isn't
1829
+ provided.
1830
+ method_parameters (Optional[str]): Optional dictionary of the method parameters. No longer in active use.
1831
+ file_extension (Optional[str]): Optional file extension. The empty string ``""`` will be assigned as the file extension if one
1832
+ isn't provided. Files with a ``.jpeg``, ``.jpg``, or ``.png`` extension will be saved to the **Images** tab.
1833
+ tags (Optional[List[str]]): Optional list of tags to allow for tag-based filtering when retrieving data.
1834
+ dataset_ids (Optional[List[str]]): Optional list of datasets to add the data to.
1835
+
1836
+ Raises:
1837
+ GRPCError: If an invalid part ID is passed.
1838
+
1839
+ Returns:
1840
+ str: Binary data ID of the new file.
1841
+
1842
+ For more information, see `Data Client API <https://docs.viam.com/dev/reference/apis/data-client/#fileupload>`_.
1843
+ """
1844
+ metadata = UploadMetadata(
1845
+ part_id=part_id,
1846
+ component_type=component_type if component_type else "",
1847
+ component_name=component_name if component_name else "",
1848
+ method_name=method_name if method_name else "",
1849
+ type=DataType.DATA_TYPE_FILE,
1850
+ file_name=file_name if file_name else "",
1851
+ method_parameters=method_parameters,
1852
+ file_extension=file_extension if file_extension else "",
1853
+ tags=tags,
1854
+ dataset_ids=dataset_ids,
1855
+ )
1856
+ response: FileUploadResponse = await self._file_upload(metadata=metadata, file_contents=FileData(data=data))
1857
+ return response.binary_data_id
1858
+
1859
+ async def file_upload_from_path(
1860
+ self,
1861
+ filepath: str,
1862
+ part_id: str,
1863
+ component_type: Optional[str] = None,
1864
+ component_name: Optional[str] = None,
1865
+ method_name: Optional[str] = None,
1866
+ method_parameters: Optional[Mapping[str, Any]] = None,
1867
+ tags: Optional[List[str]] = None,
1868
+ dataset_ids: Optional[List[str]] = None,
1869
+ ) -> str:
1870
+ """Upload arbitrary file data.
1871
+
1872
+ Upload file data that may be stored on a robot along with the relevant metadata. File data can be found in the
1873
+ **Files** tab of the **DATA** page.
1874
+
1875
+ ::
1876
+
1877
+ file_id = await data_client.file_upload_from_path(
1878
+ part_id="INSERT YOUR PART ID",
1879
+ tags=["tag_1", "tag_2"],
1880
+ dataset_ids=["dataset_1", "dataset_2"],
1881
+ filepath="/Users/<your-username>/<your-directory>/<your-file.txt>"
1882
+ )
1883
+
1884
+ Args:
1885
+ filepath (str): Absolute filepath of file to be uploaded.
1886
+ part_id (str): Part ID of the component associated with the file.
1887
+ component_type (Optional[str]): Optional type of the component associated with the file (for example, "movement_sensor").
1888
+ component_name (Optional[str]): Optional name of the component associated with the file.
1889
+ method_name (Optional[str]): Optional name of the method associated with the file.
1890
+ method_parameters (Optional[str]): Optional dictionary of the method parameters. No longer in active use.
1891
+ tags (Optional[List[str]]): Optional list of tags to allow for tag-based filtering when retrieving data.
1892
+ dataset_ids (Optional[List[str]]): Optional list of datasets to add the data to.
1893
+
1894
+ Raises:
1895
+ GRPCError: If an invalid part ID is passed.
1896
+ FileNotFoundError: If the provided filepath is not found.
1897
+
1898
+ Returns:
1899
+ str: Binary data ID of the new file.
1900
+
1901
+ For more information, see `Data Client API <https://docs.viam.com/dev/reference/apis/data-client/#fileuploadfrompath>`_.
1902
+ """
1903
+ path = Path(filepath)
1904
+ file_name = path.stem
1905
+ file_extension = path.suffix if path.suffix != "" else None
1906
+ with open(filepath, "rb") as f:
1907
+ data = f.read()
1908
+
1909
+ metadata = UploadMetadata(
1910
+ part_id=part_id,
1911
+ component_type=component_type if component_type else "",
1912
+ component_name=component_name if component_name else "",
1913
+ method_name=method_name if method_name else "",
1914
+ type=DataType.DATA_TYPE_FILE,
1915
+ file_name=file_name,
1916
+ method_parameters=method_parameters,
1917
+ file_extension=file_extension if file_extension else "",
1918
+ tags=tags,
1919
+ dataset_ids=dataset_ids,
1920
+ )
1921
+ response: FileUploadResponse = await self._file_upload(metadata=metadata, file_contents=FileData(data=data if data else bytes()))
1922
+ return response.binary_data_id
1923
+
1924
+ async def _file_upload(self, metadata: UploadMetadata, file_contents: FileData) -> FileUploadResponse:
1925
+ request_metadata = FileUploadRequest(metadata=metadata)
1926
+ request_file_contents = FileUploadRequest(file_contents=file_contents)
1927
+ stream: Stream[FileUploadRequest, FileUploadResponse]
1928
+ async with self._data_sync_client.FileUpload.open(metadata=self._metadata) as stream:
1929
+ await stream.send_message(request_metadata)
1930
+ await stream.send_message(request_file_contents, end=True)
1931
+ response = await stream.recv_message()
1932
+ if not response:
1933
+ await stream.recv_trailing_metadata() # causes us to throw appropriate gRPC error.
1934
+ raise TypeError("Response cannot be empty")
1935
+ return response
1936
+
1937
+ async def get_data_pipeline(self, id: str) -> DataPipeline:
1938
+ """Get a data pipeline by its ID.
1939
+
1940
+ ::
1941
+
1942
+ data_pipeline = await data_client.get_data_pipeline(id="<YOUR-DATA-PIPELINE-ID>")
1943
+
1944
+ Args:
1945
+ id (str): The ID of the data pipeline to get.
1946
+
1947
+ Returns:
1948
+ DataPipeline: The data pipeline with the given ID.
1949
+ """
1950
+ request = GetDataPipelineRequest(id=id)
1951
+ response: GetDataPipelineResponse = await self._data_pipelines_client.GetDataPipeline(request, metadata=self._metadata)
1952
+ return DataClient.DataPipeline.from_proto(response.data_pipeline)
1953
+
1954
+ async def list_data_pipelines(self, organization_id: str) -> List[DataPipeline]:
1955
+ """List all of the data pipelines for an organization.
1956
+
1957
+ ::
1958
+
1959
+ data_pipelines = await data_client.list_data_pipelines(organization_id="<YOUR-ORGANIZATION-ID>")
1960
+
1961
+ Args:
1962
+ organization_id (str): The ID of the organization that owns the pipelines.
1963
+ You can obtain your organization ID from the organization settings page.
1964
+
1965
+ Returns:
1966
+ List[DataPipeline]: A list of all of the data pipelines for the given organization.
1967
+ """
1968
+ request = ListDataPipelinesRequest(organization_id=organization_id)
1969
+ response: ListDataPipelinesResponse = await self._data_pipelines_client.ListDataPipelines(request, metadata=self._metadata)
1970
+ return [DataClient.DataPipeline.from_proto(pipeline) for pipeline in response.data_pipelines]
1971
+
1972
+ async def create_data_pipeline(
1973
+ self,
1974
+ organization_id: str,
1975
+ name: str,
1976
+ mql_binary: List[Dict[str, Any]],
1977
+ schedule: str,
1978
+ enable_backfill: bool,
1979
+ data_source_type: TabularDataSourceType.ValueType = TabularDataSourceType.TABULAR_DATA_SOURCE_TYPE_STANDARD,
1980
+ ) -> str:
1981
+ """Create a new data pipeline.
1982
+
1983
+ ::
1984
+
1985
+ data_pipeline_id = await data_client.create_data_pipeline(
1986
+ organization_id="<YOUR-ORGANIZATION-ID>",
1987
+ name="<YOUR-PIPELINE-NAME>",
1988
+ mql_binary=[<YOUR-MQL-PIPELINE-AGGREGATION>],
1989
+ schedule="<YOUR-SCHEDULE>",
1990
+ enable_backfill=False,
1991
+ data_source_type=TabularDataSourceType.TABULAR_DATA_SOURCE_TYPE_STANDARD,
1992
+ )
1993
+
1994
+ Args:
1995
+ organization_id (str): The ID of the organization that will own the pipeline.
1996
+ You can obtain your organization ID from the organization settings page.
1997
+ name (str): The name of the pipeline.
1998
+ mql_binary (List[Dict[str, Any]]):The MQL pipeline to run, as a list of MongoDB aggregation pipeline stages.
1999
+ schedule (str): A cron expression representing the expected execution schedule in UTC (note this also
2000
+ defines the input time window; an hourly schedule would process 1 hour of data at a time).
2001
+ enable_backfill (bool): When true, pipeline runs will be scheduled for the organization's past data.
2002
+ data_source_type (TabularDataSourceType): The type of data source to use for the pipeline.
2003
+ Defaults to TabularDataSourceType.TABULAR_DATA_SOURCE_TYPE_STANDARD.
2004
+
2005
+ Returns:
2006
+ str: The ID of the newly created pipeline.
2007
+ """
2008
+ binary: List[bytes] = [bson.encode(query) for query in mql_binary]
2009
+ request = CreateDataPipelineRequest(
2010
+ organization_id=organization_id,
2011
+ name=name,
2012
+ mql_binary=binary,
2013
+ schedule=schedule,
2014
+ enable_backfill=enable_backfill,
2015
+ data_source_type=data_source_type,
2016
+ )
2017
+ response: CreateDataPipelineResponse = await self._data_pipelines_client.CreateDataPipeline(request, metadata=self._metadata)
2018
+ return response.id
2019
+
2020
+ async def rename_data_pipeline(self, id: str, name: str) -> None:
2021
+ """Rename a data pipeline by its ID.
2022
+ ::
2023
+
2024
+ await data_client.rename_data_pipeline(id="<YOUR-DATA-PIPELINE-ID>", name="<YOUR-NEW-NAME>")
2025
+
2026
+ Args:
2027
+ id (str): The ID of the data pipeline to rename.
2028
+ name (str): The new name of the data pipeline.
2029
+ """
2030
+ if not id or not name:
2031
+ raise ValueError("id and name are required")
2032
+ request = RenameDataPipelineRequest(id=id, name=name)
2033
+ await self._data_pipelines_client.RenameDataPipeline(request, metadata=self._metadata)
2034
+
2035
+ async def delete_data_pipeline(self, id: str) -> None:
2036
+ """Delete a data pipeline by its ID.
2037
+
2038
+ ::
2039
+
2040
+ await data_client.delete_data_pipeline(id="<YOUR-DATA-PIPELINE-ID>")
2041
+
2042
+ Args:
2043
+ id (str): The ID of the data pipeline to delete.
2044
+ """
2045
+ request = DeleteDataPipelineRequest(id=id)
2046
+ await self._data_pipelines_client.DeleteDataPipeline(request, metadata=self._metadata)
2047
+
2048
+ async def list_data_pipeline_runs(self, id: str, page_size: int = 10) -> DataPipelineRunsPage:
2049
+ """List all of the data pipeline runs for a data pipeline.
2050
+
2051
+ ::
2052
+
2053
+ data_pipeline_runs = await data_client.list_data_pipeline_runs(id="<YOUR-DATA-PIPELINE-ID>")
2054
+ while len(data_pipeline_runs.runs) > 0:
2055
+ data_pipeline_runs = await data_pipeline_runs.next_page()
2056
+
2057
+ Args:
2058
+ id (str): The ID of the pipeline to list runs for
2059
+ page_size (int): The number of runs to return per page. Defaults to 10.
2060
+
2061
+ Returns:
2062
+ DataPipelineRunsPage: A page of data pipeline runs with pagination support
2063
+ """
2064
+ return await self._list_data_pipeline_runs(id, page_size)
2065
+
2066
+ async def _list_data_pipeline_runs(self, id: str, page_size: int, page_token: str = "") -> DataPipelineRunsPage:
2067
+ """Internal method to list data pipeline runs with pagination.
2068
+
2069
+ Args:
2070
+ id (str): The ID of the pipeline to list runs for
2071
+ page_size (int): The number of runs to return per page
2072
+ page_token (str): The token to use to get the next page of results
2073
+
2074
+ Returns:
2075
+ DataPipelineRunsPage: A page of data pipeline runs with pagination support
2076
+ """
2077
+ request = ListDataPipelineRunsRequest(id=id, page_size=page_size, page_token=page_token)
2078
+ response: ListDataPipelineRunsResponse = await self._data_pipelines_client.ListDataPipelineRuns(request, metadata=self._metadata)
2079
+ return DataClient.DataPipelineRunsPage.from_proto(response, self, page_size)
2080
+
2081
+ async def create_index(
2082
+ self,
2083
+ organization_id: str,
2084
+ collection_type: IndexableCollection.ValueType,
2085
+ index_spec: Dict[str, Any],
2086
+ pipeline_name: Optional[str] = None,
2087
+ ) -> None:
2088
+ """Starts a custom index build.
2089
+
2090
+ Args:
2091
+ organization_id (str): The ID of the organization that owns the data.
2092
+ To find your organization ID, visit the organization settings page.
2093
+ collection_type (IndexableCollection.ValueType): The type of collection the index is on.
2094
+ index_spec (List[Dict[str, Any]]): The MongoDB index specification defined in JSON format.
2095
+ pipeline_name (Optional[str]): The name of the pipeline if the collection type is PIPELINE_SINK.
2096
+
2097
+ For more information, see `Data Client API <https://docs.viam.com/dev/reference/apis/data-client/#createindex>`_.
2098
+ """
2099
+ index_spec_bytes = [bson.encode(index_spec)]
2100
+ request = CreateIndexRequest(
2101
+ organization_id=organization_id,
2102
+ collection_type=collection_type,
2103
+ index_spec=index_spec_bytes,
2104
+ pipeline_name=pipeline_name,
2105
+ )
2106
+ await self._data_client.CreateIndex(request, metadata=self._metadata)
2107
+
2108
+ async def list_indexes(
2109
+ self,
2110
+ organization_id: str,
2111
+ collection_type: IndexableCollection.ValueType,
2112
+ pipeline_name: Optional[str] = None,
2113
+ ) -> Sequence[Index]:
2114
+ """Returns all the indexes for a given collection.
2115
+
2116
+ Args:
2117
+ organization_id (str): The ID of the organization that owns the data.
2118
+ To find your organization ID, visit the organization settings page.
2119
+ collection_type (IndexableCollection.ValueType): The type of collection the index is on.
2120
+ pipeline_name (Optional[str]): The name of the pipeline if the collection type is PIPELINE_SINK.
2121
+
2122
+ Returns:
2123
+ List[Index]: A list of indexes.
2124
+
2125
+ For more information, see `Data Client API <https://docs.viam.com/dev/reference/apis/data-client/#listindexes>`_.
2126
+ """
2127
+ request = ListIndexesRequest(
2128
+ organization_id=organization_id,
2129
+ collection_type=collection_type,
2130
+ pipeline_name=pipeline_name,
2131
+ )
2132
+ response: ListIndexesResponse = await self._data_client.ListIndexes(request, metadata=self._metadata)
2133
+ return response.indexes
2134
+
2135
+ async def delete_index(
2136
+ self,
2137
+ organization_id: str,
2138
+ collection_type: IndexableCollection.ValueType,
2139
+ index_name: str,
2140
+ pipeline_name: Optional[str] = None,
2141
+ ) -> None:
2142
+ """Drops the specified custom index from a collection.
2143
+
2144
+ Args:
2145
+ organization_id (str): The ID of the organization that owns the data.
2146
+ To find your organization ID, visit the organization settings page.
2147
+ collection_type (IndexableCollection.ValueType): The type of collection the index is on.
2148
+ index_name (str): The name of the index to delete.
2149
+ pipeline_name (Optional[str]): The name of the pipeline if the collection type is PIPELINE_SINK.
2150
+
2151
+ For more information, see `Data Client API <https://docs.viam.com/dev/reference/apis/data-client/#deleteindex>`_.
2152
+ """
2153
+ request = DeleteIndexRequest(
2154
+ organization_id=organization_id,
2155
+ collection_type=collection_type,
2156
+ index_name=index_name,
2157
+ pipeline_name=pipeline_name,
2158
+ )
2159
+ await self._data_client.DeleteIndex(request, metadata=self._metadata)
2160
+
2161
+ async def create_binary_data_signed_url(
2162
+ self,
2163
+ binary_data_id: str,
2164
+ expiration_minutes: Optional[int] = None,
2165
+ ) -> Tuple[str, datetime]:
2166
+ """Create a signed URL for binary data.
2167
+
2168
+ ::
2169
+
2170
+ signed_url, expires_at = await data_client.create_binary_data_signed_url(
2171
+ binary_data_id="<YOUR-BINARY-DATA-ID>",
2172
+ expiration_minutes=60
2173
+ )
2174
+
2175
+ print(f"Signed URL: {signed_url}")
2176
+ print(f"Expires at: {expires_at}")
2177
+
2178
+ Args:
2179
+ binary_data_id (str): The binary data ID of the file to create a signed URL for.
2180
+ expiration_minutes (Optional[int]): Expiration time in minutes. Defaults to 15 minutes if not specified.
2181
+ Maximum allowed is 10080 minutes (7 days).
2182
+
2183
+ Returns:
2184
+ Tuple[str, datetime]: A tuple containing:
2185
+ - ``signed_url`` (*str*): The signed URL for the binary data file.
2186
+ - ``expires_at`` (*datetime*): The expiration time of the signed URL token.
2187
+
2188
+ For more information, see `Data Client API <https://docs.viam.com/dev/reference/apis/data-client/#createbinarydatasignedurl>`_.
2189
+ """
2190
+ request = CreateBinaryDataSignedURLRequest(binary_data_id=binary_data_id)
2191
+ if expiration_minutes is not None:
2192
+ request.expiration_minutes = expiration_minutes
2193
+ response: CreateBinaryDataSignedURLResponse = await self._data_client.CreateBinaryDataSignedURL(request, metadata=self._metadata)
2194
+ return response.signed_url, response.expires_at.ToDatetime()
2195
+
2196
+ @staticmethod
2197
+ def create_filter(
2198
+ component_name: Optional[str] = None,
2199
+ component_type: Optional[str] = None,
2200
+ method: Optional[str] = None,
2201
+ robot_name: Optional[str] = None,
2202
+ robot_id: Optional[str] = None,
2203
+ part_name: Optional[str] = None,
2204
+ part_id: Optional[str] = None,
2205
+ location_ids: Optional[List[str]] = None,
2206
+ organization_ids: Optional[List[str]] = None,
2207
+ mime_type: Optional[List[str]] = None,
2208
+ start_time: Optional[datetime] = None,
2209
+ end_time: Optional[datetime] = None,
2210
+ tags: Optional[List[str]] = None,
2211
+ bbox_labels: Optional[List[str]] = None,
2212
+ dataset_id: Optional[str] = None,
2213
+ ) -> Filter:
2214
+ warnings.warn("DataClient.create_filter is deprecated. Use utils.create_filter instead.", DeprecationWarning, stacklevel=2)
2215
+ return create_filter(
2216
+ component_name,
2217
+ component_type,
2218
+ method,
2219
+ robot_name,
2220
+ robot_id,
2221
+ part_name,
2222
+ part_id,
2223
+ location_ids,
2224
+ organization_ids,
2225
+ mime_type,
2226
+ start_time,
2227
+ end_time,
2228
+ tags,
2229
+ bbox_labels,
2230
+ dataset_id,
2231
+ )