viam-sdk 0.45.2__py3-none-win_amd64.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 (476) hide show
  1. viam/__init__.py +71 -0
  2. viam/app/__init__.py +0 -0
  3. viam/app/_logs.py +34 -0
  4. viam/app/app_client.py +2525 -0
  5. viam/app/billing_client.py +143 -0
  6. viam/app/data_client.py +1715 -0
  7. viam/app/ml_training_client.py +251 -0
  8. viam/app/provisioning_client.py +95 -0
  9. viam/app/viam_client.py +260 -0
  10. viam/components/__init__.py +0 -0
  11. viam/components/arm/__init__.py +16 -0
  12. viam/components/arm/arm.py +223 -0
  13. viam/components/arm/client.py +124 -0
  14. viam/components/arm/service.py +123 -0
  15. viam/components/audio_input/__init__.py +18 -0
  16. viam/components/audio_input/audio_input.py +81 -0
  17. viam/components/audio_input/client.py +70 -0
  18. viam/components/audio_input/service.py +114 -0
  19. viam/components/base/__init__.py +13 -0
  20. viam/components/base/base.py +260 -0
  21. viam/components/base/client.py +153 -0
  22. viam/components/base/service.py +138 -0
  23. viam/components/board/__init__.py +9 -0
  24. viam/components/board/board.py +414 -0
  25. viam/components/board/client.py +241 -0
  26. viam/components/board/service.py +223 -0
  27. viam/components/button/__init__.py +10 -0
  28. viam/components/button/button.py +41 -0
  29. viam/components/button/client.py +52 -0
  30. viam/components/button/service.py +46 -0
  31. viam/components/camera/__init__.py +22 -0
  32. viam/components/camera/camera.py +138 -0
  33. viam/components/camera/client.py +98 -0
  34. viam/components/camera/service.py +105 -0
  35. viam/components/component_base.py +65 -0
  36. viam/components/encoder/__init__.py +18 -0
  37. viam/components/encoder/client.py +83 -0
  38. viam/components/encoder/encoder.py +118 -0
  39. viam/components/encoder/service.py +72 -0
  40. viam/components/gantry/__init__.py +11 -0
  41. viam/components/gantry/client.py +115 -0
  42. viam/components/gantry/gantry.py +156 -0
  43. viam/components/gantry/service.py +113 -0
  44. viam/components/generic/__init__.py +18 -0
  45. viam/components/generic/client.py +62 -0
  46. viam/components/generic/generic.py +76 -0
  47. viam/components/generic/service.py +40 -0
  48. viam/components/gripper/__init__.py +11 -0
  49. viam/components/gripper/client.py +85 -0
  50. viam/components/gripper/gripper.py +114 -0
  51. viam/components/gripper/service.py +81 -0
  52. viam/components/input/__init__.py +15 -0
  53. viam/components/input/client.py +194 -0
  54. viam/components/input/input.py +297 -0
  55. viam/components/input/service.py +175 -0
  56. viam/components/motor/__init__.py +11 -0
  57. viam/components/motor/client.py +168 -0
  58. viam/components/motor/motor.py +301 -0
  59. viam/components/motor/service.py +150 -0
  60. viam/components/movement_sensor/__init__.py +21 -0
  61. viam/components/movement_sensor/client.py +161 -0
  62. viam/components/movement_sensor/movement_sensor.py +253 -0
  63. viam/components/movement_sensor/service.py +146 -0
  64. viam/components/pose_tracker/__init__.py +17 -0
  65. viam/components/pose_tracker/client.py +50 -0
  66. viam/components/pose_tracker/pose_tracker.py +40 -0
  67. viam/components/pose_tracker/service.py +45 -0
  68. viam/components/power_sensor/__init__.py +17 -0
  69. viam/components/power_sensor/client.py +86 -0
  70. viam/components/power_sensor/power_sensor.py +104 -0
  71. viam/components/power_sensor/service.py +72 -0
  72. viam/components/sensor/__init__.py +18 -0
  73. viam/components/sensor/client.py +49 -0
  74. viam/components/sensor/sensor.py +48 -0
  75. viam/components/sensor/service.py +51 -0
  76. viam/components/servo/__init__.py +11 -0
  77. viam/components/servo/client.py +86 -0
  78. viam/components/servo/service.py +80 -0
  79. viam/components/servo/servo.py +114 -0
  80. viam/components/switch/__init__.py +10 -0
  81. viam/components/switch/client.py +83 -0
  82. viam/components/switch/service.py +72 -0
  83. viam/components/switch/switch.py +95 -0
  84. viam/errors.py +105 -0
  85. viam/gen/__init__.py +0 -0
  86. viam/gen/app/__init__.py +0 -0
  87. viam/gen/app/agent/__init__.py +0 -0
  88. viam/gen/app/agent/v1/__init__.py +0 -0
  89. viam/gen/app/agent/v1/agent_grpc.py +29 -0
  90. viam/gen/app/agent/v1/agent_pb2.py +47 -0
  91. viam/gen/app/agent/v1/agent_pb2.pyi +280 -0
  92. viam/gen/app/cloudslam/__init__.py +0 -0
  93. viam/gen/app/cloudslam/v1/__init__.py +0 -0
  94. viam/gen/app/cloudslam/v1/cloud_slam_grpc.py +70 -0
  95. viam/gen/app/cloudslam/v1/cloud_slam_pb2.py +54 -0
  96. viam/gen/app/cloudslam/v1/cloud_slam_pb2.pyi +384 -0
  97. viam/gen/app/data/__init__.py +0 -0
  98. viam/gen/app/data/v1/__init__.py +0 -0
  99. viam/gen/app/data/v1/data_grpc.py +206 -0
  100. viam/gen/app/data/v1/data_pb2.py +178 -0
  101. viam/gen/app/data/v1/data_pb2.pyi +1485 -0
  102. viam/gen/app/datapipelines/__init__.py +0 -0
  103. viam/gen/app/datapipelines/v1/__init__.py +0 -0
  104. viam/gen/app/datapipelines/v1/data_pipelines_grpc.py +84 -0
  105. viam/gen/app/datapipelines/v1/data_pipelines_pb2.py +56 -0
  106. viam/gen/app/datapipelines/v1/data_pipelines_pb2.pyi +370 -0
  107. viam/gen/app/dataset/__init__.py +0 -0
  108. viam/gen/app/dataset/v1/__init__.py +0 -0
  109. viam/gen/app/dataset/v1/dataset_grpc.py +60 -0
  110. viam/gen/app/dataset/v1/dataset_pb2.py +40 -0
  111. viam/gen/app/dataset/v1/dataset_pb2.pyi +179 -0
  112. viam/gen/app/datasync/__init__.py +0 -0
  113. viam/gen/app/datasync/v1/__init__.py +0 -0
  114. viam/gen/app/datasync/v1/data_sync_grpc.py +47 -0
  115. viam/gen/app/datasync/v1/data_sync_pb2.py +70 -0
  116. viam/gen/app/datasync/v1/data_sync_pb2.pyi +425 -0
  117. viam/gen/app/mlinference/__init__.py +0 -0
  118. viam/gen/app/mlinference/v1/__init__.py +0 -0
  119. viam/gen/app/mlinference/v1/ml_inference_grpc.py +28 -0
  120. viam/gen/app/mlinference/v1/ml_inference_pb2.py +23 -0
  121. viam/gen/app/mlinference/v1/ml_inference_pb2.pyi +63 -0
  122. viam/gen/app/mltraining/__init__.py +0 -0
  123. viam/gen/app/mltraining/v1/__init__.py +0 -0
  124. viam/gen/app/mltraining/v1/ml_training_grpc.py +78 -0
  125. viam/gen/app/mltraining/v1/ml_training_pb2.py +124 -0
  126. viam/gen/app/mltraining/v1/ml_training_pb2.pyi +415 -0
  127. viam/gen/app/packages/__init__.py +0 -0
  128. viam/gen/app/packages/v1/__init__.py +0 -0
  129. viam/gen/app/packages/v1/packages_grpc.py +54 -0
  130. viam/gen/app/packages/v1/packages_pb2.py +52 -0
  131. viam/gen/app/packages/v1/packages_pb2.pyi +311 -0
  132. viam/gen/app/v1/__init__.py +0 -0
  133. viam/gen/app/v1/app_grpc.py +863 -0
  134. viam/gen/app/v1/app_pb2.py +649 -0
  135. viam/gen/app/v1/app_pb2.pyi +5279 -0
  136. viam/gen/app/v1/billing_grpc.py +76 -0
  137. viam/gen/app/v1/billing_pb2.py +92 -0
  138. viam/gen/app/v1/billing_pb2.pyi +463 -0
  139. viam/gen/app/v1/end_user_grpc.py +59 -0
  140. viam/gen/app/v1/end_user_pb2.py +55 -0
  141. viam/gen/app/v1/end_user_pb2.pyi +181 -0
  142. viam/gen/app/v1/robot_grpc.py +55 -0
  143. viam/gen/app/v1/robot_pb2.py +127 -0
  144. viam/gen/app/v1/robot_pb2.pyi +1202 -0
  145. viam/gen/common/__init__.py +0 -0
  146. viam/gen/common/v1/__init__.py +0 -0
  147. viam/gen/common/v1/common_grpc.py +0 -0
  148. viam/gen/common/v1/common_pb2.py +78 -0
  149. viam/gen/common/v1/common_pb2.pyi +687 -0
  150. viam/gen/component/__init__.py +0 -0
  151. viam/gen/component/arm/__init__.py +0 -0
  152. viam/gen/component/arm/v1/__init__.py +0 -0
  153. viam/gen/component/arm/v1/arm_grpc.py +102 -0
  154. viam/gen/component/arm/v1/arm_pb2.py +74 -0
  155. viam/gen/component/arm/v1/arm_pb2.pyi +344 -0
  156. viam/gen/component/audioinput/__init__.py +0 -0
  157. viam/gen/component/audioinput/v1/__init__.py +0 -0
  158. viam/gen/component/audioinput/v1/audioinput_grpc.py +63 -0
  159. viam/gen/component/audioinput/v1/audioinput_pb2.py +45 -0
  160. viam/gen/component/audioinput/v1/audioinput_pb2.pyi +179 -0
  161. viam/gen/component/base/__init__.py +0 -0
  162. viam/gen/component/base/v1/__init__.py +0 -0
  163. viam/gen/component/base/v1/base_grpc.py +94 -0
  164. viam/gen/component/base/v1/base_pb2.py +66 -0
  165. viam/gen/component/base/v1/base_pb2.pyi +258 -0
  166. viam/gen/component/board/__init__.py +0 -0
  167. viam/gen/component/board/v1/__init__.py +0 -0
  168. viam/gen/component/board/v1/board_grpc.py +127 -0
  169. viam/gen/component/board/v1/board_pb2.py +103 -0
  170. viam/gen/component/board/v1/board_pb2.pyi +496 -0
  171. viam/gen/component/button/__init__.py +0 -0
  172. viam/gen/component/button/v1/__init__.py +0 -0
  173. viam/gen/component/button/v1/button_grpc.py +38 -0
  174. viam/gen/component/button/v1/button_pb2.py +28 -0
  175. viam/gen/component/button/v1/button_pb2.pyi +39 -0
  176. viam/gen/component/camera/__init__.py +0 -0
  177. viam/gen/component/camera/v1/__init__.py +0 -0
  178. viam/gen/component/camera/v1/camera_grpc.py +79 -0
  179. viam/gen/component/camera/v1/camera_pb2.py +67 -0
  180. viam/gen/component/camera/v1/camera_pb2.pyi +373 -0
  181. viam/gen/component/encoder/__init__.py +0 -0
  182. viam/gen/component/encoder/v1/__init__.py +0 -0
  183. viam/gen/component/encoder/v1/encoder_grpc.py +62 -0
  184. viam/gen/component/encoder/v1/encoder_pb2.py +44 -0
  185. viam/gen/component/encoder/v1/encoder_pb2.pyi +147 -0
  186. viam/gen/component/gantry/__init__.py +0 -0
  187. viam/gen/component/gantry/v1/__init__.py +0 -0
  188. viam/gen/component/gantry/v1/gantry_grpc.py +86 -0
  189. viam/gen/component/gantry/v1/gantry_pb2.py +62 -0
  190. viam/gen/component/gantry/v1/gantry_pb2.pyi +239 -0
  191. viam/gen/component/generic/__init__.py +0 -0
  192. viam/gen/component/generic/v1/__init__.py +0 -0
  193. viam/gen/component/generic/v1/generic_grpc.py +37 -0
  194. viam/gen/component/generic/v1/generic_pb2.py +23 -0
  195. viam/gen/component/generic/v1/generic_pb2.pyi +6 -0
  196. viam/gen/component/gripper/__init__.py +0 -0
  197. viam/gen/component/gripper/v1/__init__.py +0 -0
  198. viam/gen/component/gripper/v1/gripper_grpc.py +70 -0
  199. viam/gen/component/gripper/v1/gripper_pb2.py +48 -0
  200. viam/gen/component/gripper/v1/gripper_pb2.pyi +137 -0
  201. viam/gen/component/inputcontroller/__init__.py +0 -0
  202. viam/gen/component/inputcontroller/v1/__init__.py +0 -0
  203. viam/gen/component/inputcontroller/v1/input_controller_grpc.py +71 -0
  204. viam/gen/component/inputcontroller/v1/input_controller_pb2.py +55 -0
  205. viam/gen/component/inputcontroller/v1/input_controller_pb2.pyi +243 -0
  206. viam/gen/component/motor/__init__.py +0 -0
  207. viam/gen/component/motor/v1/__init__.py +0 -0
  208. viam/gen/component/motor/v1/motor_grpc.py +118 -0
  209. viam/gen/component/motor/v1/motor_pb2.py +86 -0
  210. viam/gen/component/motor/v1/motor_pb2.pyi +368 -0
  211. viam/gen/component/movementsensor/__init__.py +0 -0
  212. viam/gen/component/movementsensor/v1/__init__.py +0 -0
  213. viam/gen/component/movementsensor/v1/movementsensor_grpc.py +110 -0
  214. viam/gen/component/movementsensor/v1/movementsensor_pb2.py +78 -0
  215. viam/gen/component/movementsensor/v1/movementsensor_pb2.pyi +384 -0
  216. viam/gen/component/posetracker/__init__.py +0 -0
  217. viam/gen/component/posetracker/v1/__init__.py +0 -0
  218. viam/gen/component/posetracker/v1/pose_tracker_grpc.py +46 -0
  219. viam/gen/component/posetracker/v1/pose_tracker_pb2.py +34 -0
  220. viam/gen/component/posetracker/v1/pose_tracker_pb2.pyi +79 -0
  221. viam/gen/component/powersensor/__init__.py +0 -0
  222. viam/gen/component/powersensor/v1/__init__.py +0 -0
  223. viam/gen/component/powersensor/v1/powersensor_grpc.py +62 -0
  224. viam/gen/component/powersensor/v1/powersensor_pb2.py +42 -0
  225. viam/gen/component/powersensor/v1/powersensor_pb2.pyi +124 -0
  226. viam/gen/component/sensor/__init__.py +0 -0
  227. viam/gen/component/sensor/v1/__init__.py +0 -0
  228. viam/gen/component/sensor/v1/sensor_grpc.py +45 -0
  229. viam/gen/component/sensor/v1/sensor_pb2.py +25 -0
  230. viam/gen/component/sensor/v1/sensor_pb2.pyi +6 -0
  231. viam/gen/component/servo/__init__.py +0 -0
  232. viam/gen/component/servo/v1/__init__.py +0 -0
  233. viam/gen/component/servo/v1/servo_grpc.py +70 -0
  234. viam/gen/component/servo/v1/servo_pb2.py +50 -0
  235. viam/gen/component/servo/v1/servo_pb2.pyi +150 -0
  236. viam/gen/component/switch/__init__.py +0 -0
  237. viam/gen/component/switch/v1/__init__.py +0 -0
  238. viam/gen/component/switch/v1/switch_grpc.py +54 -0
  239. viam/gen/component/switch/v1/switch_pb2.py +40 -0
  240. viam/gen/component/switch/v1/switch_pb2.pyi +109 -0
  241. viam/gen/component/testecho/__init__.py +0 -0
  242. viam/gen/component/testecho/v1/__init__.py +0 -0
  243. viam/gen/component/testecho/v1/testecho_grpc.py +52 -0
  244. viam/gen/component/testecho/v1/testecho_pb2.py +36 -0
  245. viam/gen/component/testecho/v1/testecho_pb2.pyi +114 -0
  246. viam/gen/module/__init__.py +0 -0
  247. viam/gen/module/v1/__init__.py +0 -0
  248. viam/gen/module/v1/module_grpc.py +61 -0
  249. viam/gen/module/v1/module_pb2.py +43 -0
  250. viam/gen/module/v1/module_pb2.pyi +211 -0
  251. viam/gen/proto/__init__.py +0 -0
  252. viam/gen/proto/rpc/__init__.py +0 -0
  253. viam/gen/proto/rpc/examples/__init__.py +0 -0
  254. viam/gen/proto/rpc/examples/echo/__init__.py +0 -0
  255. viam/gen/proto/rpc/examples/echo/v1/__init__.py +0 -0
  256. viam/gen/proto/rpc/examples/echo/v1/echo_grpc.py +44 -0
  257. viam/gen/proto/rpc/examples/echo/v1/echo_pb2.py +32 -0
  258. viam/gen/proto/rpc/examples/echo/v1/echo_pb2.pyi +87 -0
  259. viam/gen/proto/rpc/examples/echoresource/__init__.py +0 -0
  260. viam/gen/proto/rpc/examples/echoresource/v1/__init__.py +0 -0
  261. viam/gen/proto/rpc/examples/echoresource/v1/echoresource_grpc.py +43 -0
  262. viam/gen/proto/rpc/examples/echoresource/v1/echoresource_pb2.py +29 -0
  263. viam/gen/proto/rpc/examples/echoresource/v1/echoresource_pb2.pyi +93 -0
  264. viam/gen/proto/rpc/v1/__init__.py +0 -0
  265. viam/gen/proto/rpc/v1/auth_grpc.py +47 -0
  266. viam/gen/proto/rpc/v1/auth_pb2.py +34 -0
  267. viam/gen/proto/rpc/v1/auth_pb2.pyi +92 -0
  268. viam/gen/proto/rpc/webrtc/__init__.py +0 -0
  269. viam/gen/proto/rpc/webrtc/v1/__init__.py +0 -0
  270. viam/gen/proto/rpc/webrtc/v1/grpc_grpc.py +0 -0
  271. viam/gen/proto/rpc/webrtc/v1/grpc_pb2.py +43 -0
  272. viam/gen/proto/rpc/webrtc/v1/grpc_pb2.pyi +304 -0
  273. viam/gen/proto/rpc/webrtc/v1/signaling_grpc.py +54 -0
  274. viam/gen/proto/rpc/webrtc/v1/signaling_pb2.py +70 -0
  275. viam/gen/proto/rpc/webrtc/v1/signaling_pb2.pyi +496 -0
  276. viam/gen/provisioning/__init__.py +0 -0
  277. viam/gen/provisioning/v1/__init__.py +0 -0
  278. viam/gen/provisioning/v1/provisioning_grpc.py +51 -0
  279. viam/gen/provisioning/v1/provisioning_pb2.py +39 -0
  280. viam/gen/provisioning/v1/provisioning_pb2.pyi +188 -0
  281. viam/gen/robot/__init__.py +0 -0
  282. viam/gen/robot/v1/__init__.py +0 -0
  283. viam/gen/robot/v1/robot_grpc.py +208 -0
  284. viam/gen/robot/v1/robot_pb2.py +188 -0
  285. viam/gen/robot/v1/robot_pb2.pyi +1020 -0
  286. viam/gen/service/__init__.py +0 -0
  287. viam/gen/service/datamanager/__init__.py +0 -0
  288. viam/gen/service/datamanager/v1/__init__.py +0 -0
  289. viam/gen/service/datamanager/v1/data_manager_grpc.py +38 -0
  290. viam/gen/service/datamanager/v1/data_manager_pb2.py +28 -0
  291. viam/gen/service/datamanager/v1/data_manager_pb2.pyi +39 -0
  292. viam/gen/service/discovery/__init__.py +0 -0
  293. viam/gen/service/discovery/v1/__init__.py +0 -0
  294. viam/gen/service/discovery/v1/discovery_grpc.py +39 -0
  295. viam/gen/service/discovery/v1/discovery_pb2.py +29 -0
  296. viam/gen/service/discovery/v1/discovery_pb2.pyi +51 -0
  297. viam/gen/service/generic/__init__.py +0 -0
  298. viam/gen/service/generic/v1/__init__.py +0 -0
  299. viam/gen/service/generic/v1/generic_grpc.py +29 -0
  300. viam/gen/service/generic/v1/generic_pb2.py +21 -0
  301. viam/gen/service/generic/v1/generic_pb2.pyi +6 -0
  302. viam/gen/service/mlmodel/__init__.py +0 -0
  303. viam/gen/service/mlmodel/v1/__init__.py +0 -0
  304. viam/gen/service/mlmodel/v1/mlmodel_grpc.py +37 -0
  305. viam/gen/service/mlmodel/v1/mlmodel_pb2.py +83 -0
  306. viam/gen/service/mlmodel/v1/mlmodel_pb2.pyi +480 -0
  307. viam/gen/service/motion/__init__.py +0 -0
  308. viam/gen/service/motion/v1/__init__.py +0 -0
  309. viam/gen/service/motion/v1/motion_grpc.py +87 -0
  310. viam/gen/service/motion/v1/motion_pb2.py +97 -0
  311. viam/gen/service/motion/v1/motion_pb2.pyi +838 -0
  312. viam/gen/service/navigation/__init__.py +0 -0
  313. viam/gen/service/navigation/v1/__init__.py +0 -0
  314. viam/gen/service/navigation/v1/navigation_grpc.py +102 -0
  315. viam/gen/service/navigation/v1/navigation_pb2.py +84 -0
  316. viam/gen/service/navigation/v1/navigation_pb2.pyi +419 -0
  317. viam/gen/service/sensors/__init__.py +0 -0
  318. viam/gen/service/sensors/v1/__init__.py +0 -0
  319. viam/gen/service/sensors/v1/sensors_grpc.py +46 -0
  320. viam/gen/service/sensors/v1/sensors_pb2.py +68 -0
  321. viam/gen/service/sensors/v1/sensors_pb2.pyi +137 -0
  322. viam/gen/service/shell/__init__.py +0 -0
  323. viam/gen/service/shell/v1/__init__.py +0 -0
  324. viam/gen/service/shell/v1/shell_grpc.py +55 -0
  325. viam/gen/service/shell/v1/shell_pb2.py +45 -0
  326. viam/gen/service/shell/v1/shell_pb2.pyi +307 -0
  327. viam/gen/service/slam/__init__.py +0 -0
  328. viam/gen/service/slam/v1/__init__.py +0 -0
  329. viam/gen/service/slam/v1/slam_grpc.py +61 -0
  330. viam/gen/service/slam/v1/slam_pb2.py +51 -0
  331. viam/gen/service/slam/v1/slam_pb2.pyi +213 -0
  332. viam/gen/service/vision/__init__.py +0 -0
  333. viam/gen/service/vision/v1/__init__.py +0 -0
  334. viam/gen/service/vision/v1/vision_grpc.py +87 -0
  335. viam/gen/service/vision/v1/vision_pb2.py +69 -0
  336. viam/gen/service/vision/v1/vision_pb2.pyi +454 -0
  337. viam/gen/stream/__init__.py +0 -0
  338. viam/gen/stream/v1/__init__.py +0 -0
  339. viam/gen/stream/v1/stream_grpc.py +59 -0
  340. viam/gen/stream/v1/stream_pb2.py +39 -0
  341. viam/gen/stream/v1/stream_pb2.pyi +161 -0
  342. viam/gen/tagger/__init__.py +0 -0
  343. viam/gen/tagger/v1/__init__.py +0 -0
  344. viam/gen/tagger/v1/tagger_grpc.py +0 -0
  345. viam/gen/tagger/v1/tagger_pb2.py +16 -0
  346. viam/gen/tagger/v1/tagger_pb2.pyi +15 -0
  347. viam/logging.py +216 -0
  348. viam/media/__init__.py +0 -0
  349. viam/media/audio.py +16 -0
  350. viam/media/utils/__init__.py +0 -0
  351. viam/media/utils/pil/__init__.py +51 -0
  352. viam/media/utils/pil/viam_rgba_plugin.py +73 -0
  353. viam/media/viam_rgba.py +10 -0
  354. viam/media/video.py +217 -0
  355. viam/module/__init__.py +5 -0
  356. viam/module/module.py +281 -0
  357. viam/module/service.py +66 -0
  358. viam/module/types.py +23 -0
  359. viam/operations.py +124 -0
  360. viam/proto/__init__.py +0 -0
  361. viam/proto/app/__init__.py +554 -0
  362. viam/proto/app/agent/__init__.py +28 -0
  363. viam/proto/app/billing.py +58 -0
  364. viam/proto/app/cloudslam/__init__.py +48 -0
  365. viam/proto/app/data/__init__.py +138 -0
  366. viam/proto/app/datapipelines/__init__.py +56 -0
  367. viam/proto/app/dataset/__init__.py +36 -0
  368. viam/proto/app/datasync/__init__.py +44 -0
  369. viam/proto/app/end_user.py +34 -0
  370. viam/proto/app/mlinference/__init__.py +15 -0
  371. viam/proto/app/mltraining/__init__.py +52 -0
  372. viam/proto/app/packages/__init__.py +38 -0
  373. viam/proto/app/robot.py +84 -0
  374. viam/proto/common/__init__.py +66 -0
  375. viam/proto/component/__init__.py +0 -0
  376. viam/proto/component/arm/__init__.py +48 -0
  377. viam/proto/component/audioinput/__init__.py +30 -0
  378. viam/proto/component/base/__init__.py +42 -0
  379. viam/proto/component/board/__init__.py +62 -0
  380. viam/proto/component/button/__init__.py +15 -0
  381. viam/proto/component/camera/__init__.py +46 -0
  382. viam/proto/component/encoder/__init__.py +28 -0
  383. viam/proto/component/gantry/__init__.py +40 -0
  384. viam/proto/component/generic/__init__.py +12 -0
  385. viam/proto/component/gripper/__init__.py +30 -0
  386. viam/proto/component/inputcontroller/__init__.py +38 -0
  387. viam/proto/component/motor/__init__.py +56 -0
  388. viam/proto/component/movementsensor/__init__.py +50 -0
  389. viam/proto/component/posetracker/__init__.py +19 -0
  390. viam/proto/component/powersensor/__init__.py +30 -0
  391. viam/proto/component/sensor/__init__.py +12 -0
  392. viam/proto/component/servo/__init__.py +32 -0
  393. viam/proto/component/switch/__init__.py +26 -0
  394. viam/proto/component/testecho/__init__.py +30 -0
  395. viam/proto/module/__init__.py +38 -0
  396. viam/proto/provisioning/__init__.py +36 -0
  397. viam/proto/robot/__init__.py +130 -0
  398. viam/proto/rpc/__init__.py +0 -0
  399. viam/proto/rpc/auth.py +34 -0
  400. viam/proto/rpc/examples/__init__.py +0 -0
  401. viam/proto/rpc/examples/echo/__init__.py +26 -0
  402. viam/proto/rpc/examples/echoresource/__init__.py +30 -0
  403. viam/proto/rpc/webrtc/__init__.py +0 -0
  404. viam/proto/rpc/webrtc/grpc.py +36 -0
  405. viam/proto/rpc/webrtc/signaling.py +58 -0
  406. viam/proto/service/__init__.py +0 -0
  407. viam/proto/service/datamanager/__init__.py +19 -0
  408. viam/proto/service/discovery/__init__.py +15 -0
  409. viam/proto/service/generic/__init__.py +12 -0
  410. viam/proto/service/mlmodel/__init__.py +54 -0
  411. viam/proto/service/motion/__init__.py +68 -0
  412. viam/proto/service/navigation/__init__.py +58 -0
  413. viam/proto/service/sensors/__init__.py +18 -0
  414. viam/proto/service/shell/__init__.py +36 -0
  415. viam/proto/service/slam/__init__.py +36 -0
  416. viam/proto/service/vision/__init__.py +46 -0
  417. viam/proto/stream/__init__.py +36 -0
  418. viam/proto/tagger/__init__.py +6 -0
  419. viam/py.typed +0 -0
  420. viam/resource/__init__.py +0 -0
  421. viam/resource/base.py +123 -0
  422. viam/resource/easy_resource.py +153 -0
  423. viam/resource/manager.py +126 -0
  424. viam/resource/registry.py +199 -0
  425. viam/resource/rpc_client_base.py +65 -0
  426. viam/resource/rpc_service_base.py +48 -0
  427. viam/resource/types.py +213 -0
  428. viam/robot/__init__.py +0 -0
  429. viam/robot/client.py +909 -0
  430. viam/robot/service.py +69 -0
  431. viam/rpc/__init__.py +0 -0
  432. viam/rpc/dial.py +420 -0
  433. viam/rpc/libviam_rust_utils.dll +0 -0
  434. viam/rpc/server.py +201 -0
  435. viam/rpc/signaling.py +29 -0
  436. viam/rpc/types.py +22 -0
  437. viam/services/__init__.py +0 -0
  438. viam/services/discovery/__init__.py +12 -0
  439. viam/services/discovery/client.py +55 -0
  440. viam/services/discovery/discovery.py +52 -0
  441. viam/services/discovery/service.py +43 -0
  442. viam/services/generic/__init__.py +18 -0
  443. viam/services/generic/client.py +58 -0
  444. viam/services/generic/generic.py +58 -0
  445. viam/services/generic/service.py +29 -0
  446. viam/services/mlmodel/__init__.py +24 -0
  447. viam/services/mlmodel/client.py +37 -0
  448. viam/services/mlmodel/mlmodel.py +78 -0
  449. viam/services/mlmodel/service.py +38 -0
  450. viam/services/mlmodel/utils.py +101 -0
  451. viam/services/motion/__init__.py +17 -0
  452. viam/services/motion/client.py +215 -0
  453. viam/services/motion/motion.py +378 -0
  454. viam/services/motion/service.py +132 -0
  455. viam/services/navigation/__init__.py +11 -0
  456. viam/services/navigation/client.py +99 -0
  457. viam/services/navigation/navigation.py +250 -0
  458. viam/services/navigation/service.py +137 -0
  459. viam/services/service_base.py +78 -0
  460. viam/services/service_client_base.py +46 -0
  461. viam/services/slam/__init__.py +17 -0
  462. viam/services/slam/client.py +62 -0
  463. viam/services/slam/service.py +75 -0
  464. viam/services/slam/slam.py +111 -0
  465. viam/services/vision/__init__.py +15 -0
  466. viam/services/vision/client.py +206 -0
  467. viam/services/vision/service.py +146 -0
  468. viam/services/vision/vision.py +315 -0
  469. viam/sessions_client.py +245 -0
  470. viam/streams.py +44 -0
  471. viam/utils.py +365 -0
  472. viam/version_metadata.py +4 -0
  473. viam_sdk-0.45.2.dist-info/METADATA +157 -0
  474. viam_sdk-0.45.2.dist-info/RECORD +476 -0
  475. viam_sdk-0.45.2.dist-info/WHEEL +4 -0
  476. viam_sdk-0.45.2.dist-info/licenses/LICENSE +202 -0
@@ -0,0 +1,1715 @@
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
+
11
+ from viam import logging
12
+ from viam.proto.app.data import (
13
+ AddBinaryDataToDatasetByIDsRequest,
14
+ AddBoundingBoxToImageByIDRequest,
15
+ AddBoundingBoxToImageByIDResponse,
16
+ AddTagsToBinaryDataByFilterRequest,
17
+ AddTagsToBinaryDataByIDsRequest,
18
+ BinaryData,
19
+ BinaryDataByFilterRequest,
20
+ BinaryDataByFilterResponse,
21
+ BinaryDataByIDsRequest,
22
+ BinaryDataByIDsResponse,
23
+ BinaryID,
24
+ BoundingBoxLabelsByFilterRequest,
25
+ BoundingBoxLabelsByFilterResponse,
26
+ CaptureInterval,
27
+ CaptureMetadata,
28
+ ConfigureDatabaseUserRequest,
29
+ DataRequest,
30
+ DataServiceStub,
31
+ DeleteBinaryDataByFilterRequest,
32
+ DeleteBinaryDataByFilterResponse,
33
+ DeleteBinaryDataByIDsRequest,
34
+ DeleteBinaryDataByIDsResponse,
35
+ DeleteTabularDataRequest,
36
+ DeleteTabularDataResponse,
37
+ ExportTabularDataRequest,
38
+ ExportTabularDataResponse,
39
+ Filter,
40
+ GetDatabaseConnectionRequest,
41
+ GetDatabaseConnectionResponse,
42
+ GetLatestTabularDataRequest,
43
+ GetLatestTabularDataResponse,
44
+ Order,
45
+ RemoveBinaryDataFromDatasetByIDsRequest,
46
+ RemoveBoundingBoxFromImageByIDRequest,
47
+ RemoveTagsFromBinaryDataByFilterRequest,
48
+ RemoveTagsFromBinaryDataByFilterResponse,
49
+ RemoveTagsFromBinaryDataByIDsRequest,
50
+ RemoveTagsFromBinaryDataByIDsResponse,
51
+ TabularDataByFilterRequest,
52
+ TabularDataByFilterResponse,
53
+ TabularDataByMQLRequest,
54
+ TabularDataByMQLResponse,
55
+ TabularDataBySQLRequest,
56
+ TabularDataBySQLResponse,
57
+ TagsByFilterRequest,
58
+ TagsByFilterResponse,
59
+ )
60
+ from viam.proto.app.dataset import (
61
+ CreateDatasetRequest,
62
+ CreateDatasetResponse,
63
+ Dataset,
64
+ DatasetServiceStub,
65
+ DeleteDatasetRequest,
66
+ ListDatasetsByIDsRequest,
67
+ ListDatasetsByIDsResponse,
68
+ ListDatasetsByOrganizationIDRequest,
69
+ ListDatasetsByOrganizationIDResponse,
70
+ RenameDatasetRequest,
71
+ )
72
+ from viam.proto.app.datasync import (
73
+ DataCaptureUploadMetadata,
74
+ DataCaptureUploadRequest,
75
+ DataCaptureUploadResponse,
76
+ DataSyncServiceStub,
77
+ DataType,
78
+ FileData,
79
+ FileUploadRequest,
80
+ FileUploadResponse,
81
+ SensorData,
82
+ SensorMetadata,
83
+ StreamingDataCaptureUploadRequest,
84
+ StreamingDataCaptureUploadResponse,
85
+ UploadMetadata,
86
+ )
87
+ from viam.utils import ValueTypes, _alias_param, create_filter, datetime_to_timestamp, struct_to_dict
88
+
89
+ LOGGER = logging.getLogger(__name__)
90
+
91
+
92
+ class DataClient:
93
+ """gRPC client for uploading and retrieving data from app.
94
+
95
+ Constructor is used by `ViamClient` to instantiate relevant service stubs. Calls to `DataClient` methods should be made through
96
+ `ViamClient`.
97
+
98
+ Establish a Connection::
99
+
100
+ import asyncio
101
+
102
+ from viam.rpc.dial import DialOptions, Credentials
103
+ from viam.app.viam_client import ViamClient
104
+
105
+
106
+ async def connect() -> ViamClient:
107
+ # Replace "<API-KEY>" (including brackets) with your API key and "<API-KEY-ID>" with your API key ID
108
+ dial_options = DialOptions.with_api_key("<API-KEY>", "<API-KEY-ID>")
109
+ return await ViamClient.create_from_dial_options(dial_options)
110
+
111
+
112
+ async def main():
113
+ # Make a ViamClient
114
+ viam_client = await connect()
115
+ # Instantiate a DataClient to run data client API methods on
116
+ data_client = viam_client.data_client
117
+
118
+ viam_client.close()
119
+
120
+ if __name__ == '__main__':
121
+ asyncio.run(main())
122
+
123
+
124
+ For more information, see `Data Client API <https://docs.viam.com/dev/reference/apis/data-client/>`_.
125
+ """
126
+
127
+ @dataclass
128
+ class TabularData:
129
+ """Class representing a piece of tabular data and associated metadata."""
130
+
131
+ data: Mapping[str, Any]
132
+ """The requested data"""
133
+
134
+ metadata: CaptureMetadata
135
+ """The metadata associated with the data"""
136
+
137
+ time_requested: datetime
138
+ """The time the data were requested"""
139
+
140
+ time_received: datetime
141
+ """The time the data were received"""
142
+
143
+ def __str__(self) -> str:
144
+ return f"{self.data}\n{self.metadata}Time requested: {self.time_requested}\nTime received: {self.time_received}\n"
145
+
146
+ def __eq__(self, other: object) -> bool:
147
+ if isinstance(other, DataClient.TabularData):
148
+ return str(self) == str(other)
149
+ return False
150
+
151
+ @dataclass
152
+ class TabularDataPoint:
153
+ """Represents a tabular data point and its associated metadata."""
154
+
155
+ part_id: str
156
+ """The robot part ID"""
157
+
158
+ resource_name: str
159
+ """The resource name"""
160
+
161
+ resource_api: str
162
+ """The resource API. Ex: rdk:component:sensor"""
163
+
164
+ method_name: str
165
+ """The method used for data capture. Ex: Readings"""
166
+
167
+ time_captured: datetime
168
+ """The time at which the data point was captured"""
169
+
170
+ organization_id: str
171
+ """The organization ID"""
172
+
173
+ location_id: str
174
+ """The location ID"""
175
+
176
+ robot_name: str
177
+ """The robot name"""
178
+
179
+ robot_id: str
180
+ """The robot ID"""
181
+
182
+ part_name: str
183
+ """The robot part name"""
184
+
185
+ method_parameters: Mapping[str, ValueTypes]
186
+ """Additional parameters associated with the data capture method"""
187
+
188
+ tags: List[str]
189
+ """A list of tags associated with the data point"""
190
+
191
+ payload: Mapping[str, ValueTypes]
192
+ """The captured data"""
193
+
194
+ def __str__(self) -> str:
195
+ return (
196
+ f"TabularDataPoint("
197
+ f"robot='{self.robot_name}' (id={self.robot_id}), "
198
+ f"part='{self.part_name}' (id={self.part_id}), "
199
+ f"resource='{self.resource_name}' ({self.resource_api}), "
200
+ f"method='{self.method_name}', "
201
+ f"org={self.organization_id}, "
202
+ f"location={self.location_id}, "
203
+ f"time='{self.time_captured.isoformat()}', "
204
+ f"params={self.method_parameters}, "
205
+ f"tags={self.tags}, "
206
+ f"payload={self.payload})"
207
+ )
208
+
209
+ def __eq__(self, other: object) -> bool:
210
+ if isinstance(other, DataClient.TabularDataPoint):
211
+ return str(self) == str(other)
212
+ return False
213
+
214
+ @property
215
+ def resource_subtype(self) -> str:
216
+ warnings.warn(
217
+ "`TabularDataPoint.resource_subtype` is deprecated. Use `TabularDataPoint.resource_api` instead.",
218
+ DeprecationWarning,
219
+ stacklevel=2,
220
+ )
221
+ return self.resource_api
222
+
223
+ def __init__(self, channel: Channel, metadata: Mapping[str, str]):
224
+ """Create a `DataClient` that maintains a connection to app.
225
+
226
+ Args:
227
+ channel (grpclib.client.Channel): Connection to app.
228
+ metadata (Mapping[str, str]): Required authorization token to send requests to app.
229
+ """
230
+ self._metadata = metadata
231
+ self._data_client = DataServiceStub(channel)
232
+ self._data_sync_client = DataSyncServiceStub(channel)
233
+ self._dataset_client = DatasetServiceStub(channel)
234
+ self._channel = channel
235
+
236
+ _data_client: DataServiceStub
237
+ _data_sync_client: DataSyncServiceStub
238
+ _dataset_client: DatasetServiceStub
239
+ _metadata: Mapping[str, str]
240
+ _channel: Channel
241
+
242
+ async def tabular_data_by_filter(
243
+ self,
244
+ filter: Optional[Filter] = None,
245
+ limit: Optional[int] = None,
246
+ sort_order: Optional[Order.ValueType] = None,
247
+ last: Optional[str] = None,
248
+ count_only: bool = False,
249
+ include_internal_data: bool = False,
250
+ dest: Optional[str] = None,
251
+ ) -> Tuple[List[TabularData], int, str]:
252
+ """Filter and download tabular data. The data will be paginated into pages of `limit` items, and the pagination ID will be included
253
+ in the returned tuple. If a destination is provided, the data will be saved to that file.
254
+ If the file is not empty, it will be overwritten.
255
+
256
+ ::
257
+
258
+ from viam.utils import create_filter
259
+
260
+ my_data = []
261
+ my_filter = create_filter(component_name="motor-1")
262
+ last = None
263
+ while True:
264
+ tabular_data, count, last = await data_client.tabular_data_by_filter(my_filter, last=last)
265
+ if not tabular_data:
266
+ break
267
+ my_data.extend(tabular_data)
268
+
269
+ print(f"My data: {my_data}")
270
+
271
+ Args:
272
+ filter (viam.proto.app.data.Filter): Optional `Filter` specifying tabular data to retrieve. No `Filter` implies all tabular
273
+ data.
274
+ limit (int): The maximum number of entries to include in a page. Defaults to 50 if unspecified.
275
+ sort_order (viam.proto.app.data.Order): The desired sort order of the data.
276
+ last (str): Optional string indicating the object identifier of the last-returned data.
277
+ This object identifier is returned by calls to `TabularDataByFilter` as the `last` value.
278
+ If provided, the server will return the next data entries after the last object identifier.
279
+ count_only (bool): Whether to return only the total count of entries.
280
+ include_internal_data (bool): Whether to return the internal data. Internal data is used for Viam-specific data ingestion,
281
+ like cloud SLAM. Defaults to `False`.
282
+ dest (str): Optional filepath for writing retrieved data.
283
+
284
+ Returns:
285
+ Tuple[List[TabularData], int, str]: A tuple containing the following:
286
+ List[TabularData]: The tabular data,
287
+ int: The count (number of entries),
288
+ str: The last-returned page ID.
289
+
290
+ For more information, see `Data Client API <https://docs.viam.com/dev/reference/apis/data-client/#tabulardatabyfilter>`_.
291
+ """
292
+ filter = filter if filter else Filter()
293
+
294
+ data_request = DataRequest(filter=filter)
295
+ if limit:
296
+ data_request.limit = limit
297
+ if sort_order:
298
+ data_request.sort_order = sort_order
299
+ if last:
300
+ data_request.last = last
301
+ request = TabularDataByFilterRequest(data_request=data_request, count_only=count_only, include_internal_data=include_internal_data)
302
+ response: TabularDataByFilterResponse = await self._data_client.TabularDataByFilter(request, metadata=self._metadata)
303
+ data = [
304
+ DataClient.TabularData(
305
+ struct_to_dict(struct.data),
306
+ response.metadata[struct.metadata_index],
307
+ struct.time_requested.ToDatetime(),
308
+ struct.time_received.ToDatetime(),
309
+ )
310
+ for struct in response.data
311
+ ]
312
+
313
+ if dest:
314
+ try:
315
+ file = open(dest, "w")
316
+ file.write(f"{[str(d) for d in data]}")
317
+ file.flush()
318
+ except Exception as e:
319
+ LOGGER.error(f"Failed to write tabular data to file {dest}", exc_info=e)
320
+ return data, response.count, response.last
321
+
322
+ async def tabular_data_by_sql(self, organization_id: str, sql_query: str) -> List[Dict[str, Union[ValueTypes, datetime]]]:
323
+ """Obtain unified tabular data and metadata, queried with SQL.
324
+ Make sure your API key has permissions at the organization level in order to use this.
325
+
326
+ ::
327
+
328
+ data = await data_client.tabular_data_by_sql(
329
+ organization_id="<YOUR-ORG-ID>",
330
+ sql_query="SELECT * FROM readings LIMIT 5"
331
+ )
332
+
333
+ Args:
334
+ organization_id (str): The ID of the organization that owns the data.
335
+ You can obtain your organization ID from the Viam app's organization settings page.
336
+ sql_query (str): The SQL query to run.
337
+
338
+ Returns:
339
+ List[Dict[str, Union[ValueTypes, datetime]]]: An array of decoded BSON data objects.
340
+
341
+ For more information, see `Data Client API <https://docs.viam.com/dev/reference/apis/data-client/#tabulardatabysql>`_.
342
+ """
343
+ request = TabularDataBySQLRequest(organization_id=organization_id, sql_query=sql_query)
344
+ response: TabularDataBySQLResponse = await self._data_client.TabularDataBySQL(request, metadata=self._metadata)
345
+ return [bson.decode(bson_bytes) for bson_bytes in response.raw_data]
346
+
347
+ @_alias_param("query", param_alias="mql_binary")
348
+ async def tabular_data_by_mql(
349
+ self, organization_id: str, query: Union[List[bytes], List[Dict[str, Any]]], use_recent_data: Optional[bool] = None
350
+ ) -> List[Dict[str, Union[ValueTypes, datetime]]]:
351
+ """Obtain unified tabular data and metadata, queried with MQL.
352
+
353
+ ::
354
+
355
+ import bson
356
+
357
+ tabular_data = await data_client.tabular_data_by_mql(organization_id="<YOUR-ORG-ID>", query=[
358
+ { '$match': { 'location_id': '<YOUR-LOCATION-ID>' } },
359
+ { "$limit": 5 }
360
+ ])
361
+
362
+ print(f"Tabular Data: {tabular_data}")
363
+
364
+ Args:
365
+ organization_id (str): The ID of the organization that owns the data.
366
+ You can obtain your organization ID from the Viam app's organization settings page.
367
+ query (Union[List[bytes], List[Dict[str, Any]]]): The MQL query to run, as a list of MongoDB aggregation pipeline stages.
368
+ Note: Each stage can be provided as either a dictionary or raw BSON bytes, but support for bytes will be removed in the future,
369
+ so using a dictionary is preferred.
370
+ use_recent_data (bool): Whether to query blob storage or your recent data store. Defaults to `False`
371
+
372
+ Returns:
373
+ List[Dict[str, Union[ValueTypes, datetime]]]: An array of decoded BSON data objects.
374
+
375
+ For more information, see `Data Client API <https://docs.viam.com/dev/reference/apis/data-client/#tabulardatabymql>`_.
376
+ """
377
+ binary: List[bytes] = [bson.encode(query) for query in query] if isinstance(query[0], dict) else query # type: ignore
378
+ request = TabularDataByMQLRequest(organization_id=organization_id, mql_binary=binary, use_recent_data=use_recent_data)
379
+ response: TabularDataByMQLResponse = await self._data_client.TabularDataByMQL(request, metadata=self._metadata)
380
+ return [bson.decode(bson_bytes) for bson_bytes in response.raw_data]
381
+
382
+ @_alias_param("resource_api", param_alias="resource_subtype")
383
+ async def get_latest_tabular_data(
384
+ self, part_id: str, resource_name: str, resource_api: str, method_name: str
385
+ ) -> Optional[Tuple[datetime, datetime, Dict[str, ValueTypes]]]:
386
+ """Gets the most recent tabular data captured from the specified data source, as long as it was synced within the last year.
387
+
388
+ ::
389
+
390
+ tabular_data = await data_client.get_latest_tabular_data(
391
+ part_id="77ae3145-7b91-123a-a234-e567cdca8910",
392
+ resource_name="camera-1",
393
+ resource_api="rdk:component:camera",
394
+ method_name="GetImage"
395
+ )
396
+
397
+ if tabular_data:
398
+ time_captured, time_synced, payload = tabular_data
399
+ print(f"Time Captured: {time_captured}")
400
+ print(f"Time Synced: {time_synced}")
401
+ print(f"Payload: {payload}")
402
+ else:
403
+ print(f"No data returned: {tabular_data}")
404
+
405
+ Args:
406
+ part_id (str): The ID of the part that owns the data.
407
+ resource_name (str): The name of the requested resource that captured the data. Ex: "my-sensor".
408
+ resource_api (str): The API of the requested resource that captured the data. Ex: "rdk:component:sensor".
409
+ method_name (str): The data capture method name. Ex: "Readings".
410
+
411
+ Returns:
412
+ Optional[Tuple[datetime, datetime, Dict[str, ValueTypes]]]: A return value of None means that data hasn't been synced yet for the data source
413
+ or the most recently captured data was over a year ago, otherwise the returned tuple contains the following:
414
+ datetime: The time captured,
415
+ datetime: The time synced,
416
+ Dict[str, ValueTypes]: The latest tabular data captured from the specified data source.
417
+
418
+ For more information, see `Data Client API <https://docs.viam.com/dev/reference/apis/data-client/#getlatesttabulardata>`_.
419
+ """
420
+
421
+ request = GetLatestTabularDataRequest(
422
+ part_id=part_id, resource_name=resource_name, resource_subtype=resource_api, method_name=method_name
423
+ )
424
+ response: GetLatestTabularDataResponse = await self._data_client.GetLatestTabularData(request, metadata=self._metadata)
425
+ if not response.payload:
426
+ return None
427
+ return response.time_captured.ToDatetime(), response.time_synced.ToDatetime(), struct_to_dict(response.payload)
428
+
429
+ @_alias_param("resource_api", param_alias="resource_subtype")
430
+ async def export_tabular_data(
431
+ self,
432
+ part_id: str,
433
+ resource_name: str,
434
+ resource_api: str,
435
+ method_name: str,
436
+ start_time: Optional[datetime] = None,
437
+ end_time: Optional[datetime] = None,
438
+ ) -> List[TabularDataPoint]:
439
+ """Obtain unified tabular data and metadata from the specified data source.
440
+
441
+ ::
442
+
443
+ tabular_data = await data_client.export_tabular_data(
444
+ part_id="<PART-ID>",
445
+ resource_name="<RESOURCE-NAME>",
446
+ resource_api="<RESOURCE-API>",
447
+ method_name="<METHOD-NAME>",
448
+ start_time="<START_TIME>"
449
+ end_time="<END_TIME>"
450
+ )
451
+
452
+ print(f"My data: {tabular_data}")
453
+
454
+ Args:
455
+ part_id (str): The ID of the part that owns the data.
456
+ resource_name (str): The name of the requested resource that captured the data.
457
+ resource_api (str): The API of the requested resource that captured the data.
458
+ method_name (str): The data capture method name.
459
+ start_time (datetime): Optional start time for requesting a specific range of data.
460
+ end_time (datetime): Optional end time for requesting a specific range of data.
461
+
462
+ Returns:
463
+ List[TabularDataPoint]: The unified tabular data and metadata.
464
+
465
+ For more information, see `Data Client API <https://docs.viam.com/dev/reference/apis/data-client/#exporttabulardata>`_.
466
+ """
467
+
468
+ interval = CaptureInterval(start=datetime_to_timestamp(start_time), end=datetime_to_timestamp(end_time))
469
+ request = ExportTabularDataRequest(
470
+ part_id=part_id, resource_name=resource_name, resource_subtype=resource_api, method_name=method_name, interval=interval
471
+ )
472
+ response: List[ExportTabularDataResponse] = await self._data_client.ExportTabularData(request, metadata=self._metadata)
473
+
474
+ return [
475
+ DataClient.TabularDataPoint(
476
+ part_id=resp.part_id,
477
+ resource_name=resp.resource_name,
478
+ resource_api=resp.resource_subtype,
479
+ method_name=resp.method_name,
480
+ time_captured=resp.time_captured.ToDatetime(),
481
+ organization_id=resp.organization_id,
482
+ location_id=resp.location_id,
483
+ robot_name=resp.robot_name,
484
+ robot_id=resp.robot_id,
485
+ part_name=resp.part_name,
486
+ method_parameters=struct_to_dict(resp.method_parameters),
487
+ tags=list(resp.tags),
488
+ payload=struct_to_dict(resp.payload),
489
+ )
490
+ for resp in response
491
+ ]
492
+
493
+ async def binary_data_by_filter(
494
+ self,
495
+ filter: Optional[Filter] = None,
496
+ limit: Optional[int] = None,
497
+ sort_order: Optional[Order.ValueType] = None,
498
+ last: Optional[str] = None,
499
+ include_binary_data: bool = True,
500
+ count_only: bool = False,
501
+ include_internal_data: bool = False,
502
+ dest: Optional[str] = None,
503
+ ) -> Tuple[List[BinaryData], int, str]:
504
+ """Filter and download binary data. The data will be paginated into pages of `limit` items, and the pagination ID will be included
505
+ in the returned tuple. If a destination is provided, the data will be saved to that file.
506
+ If the file is not empty, it will be overwritten.
507
+
508
+ ::
509
+
510
+ from viam.utils import create_filter
511
+ from viam.proto.app.data import Filter, TagsFilter, TagsFilterType
512
+
513
+ # Get data captured from camera components
514
+ my_data = []
515
+ last = None
516
+ my_filter = create_filter(component_name="camera-1")
517
+
518
+ while True:
519
+ data, count, last = await data_client.binary_data_by_filter(
520
+ my_filter, limit=1, last=last)
521
+ if not data:
522
+ break
523
+ my_data.extend(data)
524
+
525
+ print(f"My data: {my_data}")
526
+
527
+ # Get untagged data from a dataset
528
+
529
+ my_untagged_data = []
530
+ last = None
531
+ tags_filter = TagsFilter(type=TagsFilterType.TAGS_FILTER_TYPE_UNTAGGED)
532
+ my_filter = Filter(
533
+ dataset_id="66db6fe7d93d1ade24cd1dc3",
534
+ tags_filter=tags_filter
535
+ )
536
+
537
+ while True:
538
+ data, count, last = await data_client.binary_data_by_filter(
539
+ my_filter, last=last, include_binary_data=False)
540
+ if not data:
541
+ break
542
+ my_untagged_data.extend(data)
543
+
544
+ Args:
545
+ filter (viam.proto.app.data.Filter): Optional `Filter` specifying tabular data to retrieve. No `Filter` implies all binary
546
+ data.
547
+ limit (int): The maximum number of entries to include in a page. Defaults to 50 if unspecified.
548
+ sort_order (viam.proto.app.data.Order): The desired sort order of the data.
549
+ last (str): Optional string indicating the object identifier of the last-returned data.
550
+ This object identifier is returned by calls to `BinaryDataByFilter` as the `last` value.
551
+ If provided, the server will return the next data entries after the last object identifier.
552
+ include_binary_data (bool): Boolean specifying whether to actually include the binary file data with each retrieved file.
553
+ Defaults to true (that is, both the files' data and metadata are returned).
554
+ count_only (bool): Whether to return only the total count of entries.
555
+ include_internal_data (bool): Whether to return the internal data. Internal data is used for Viam-specific data ingestion,
556
+ like cloud SLAM. Defaults to `False`.
557
+ dest (str): Optional filepath for writing retrieved data.
558
+
559
+ Returns:
560
+ Tuple[List[viam.proto.app.data.BinaryData], int, str]: A tuple containing the following:
561
+ List[viam.proto.app.data.BinaryData]: The binary data,
562
+ int: The count (number of entries),
563
+ str: The last-returned page ID.
564
+
565
+ For more information, see `Data Client API <https://docs.viam.com/dev/reference/apis/data-client/#binarydatabyfilter>`_.
566
+ """
567
+
568
+ data_request = DataRequest(filter=filter)
569
+ if limit:
570
+ data_request.limit = limit
571
+ if sort_order:
572
+ data_request.sort_order = sort_order
573
+ if last:
574
+ data_request.last = last
575
+ request = BinaryDataByFilterRequest(
576
+ data_request=data_request,
577
+ include_binary=include_binary_data,
578
+ count_only=count_only,
579
+ include_internal_data=include_internal_data,
580
+ )
581
+ response: BinaryDataByFilterResponse = await self._data_client.BinaryDataByFilter(request, metadata=self._metadata)
582
+ data = list(response.data)
583
+ if dest:
584
+ try:
585
+ file = open(dest, "w")
586
+ file.write(f"{[str(d) for d in data]}")
587
+ file.flush()
588
+ except Exception as e:
589
+ LOGGER.error(f"Failed to write binary data to file {dest}", exc_info=e)
590
+
591
+ return data, response.count, response.last
592
+
593
+ async def binary_data_by_ids(
594
+ self,
595
+ binary_ids: Union[List[BinaryID], List[str]],
596
+ dest: Optional[str] = None,
597
+ ) -> List[BinaryData]:
598
+ """Filter and download binary data.
599
+
600
+ ::
601
+
602
+ binary_metadata, count, last = await data_client.binary_data_by_filter(
603
+ include_binary_data=False
604
+ )
605
+
606
+ my_ids = []
607
+
608
+ for obj in binary_metadata:
609
+ my_ids.append(obj.metadata.binary_data_id)
610
+
611
+ binary_data = await data_client.binary_data_by_ids(my_ids)
612
+
613
+ Args:
614
+ binary_ids (Union[List[BinaryID], List[str]]): Binary data id strings specifying the desired data or `BinaryID` objects. Must be non-empty.
615
+ Note: `BinaryID` objects are deprecated and will be removed in a future release. Please use the binary data id field instead.
616
+ dest (str): Optional filepath for writing retrieved data.
617
+
618
+ Raises:
619
+ GRPCError: If no binary data id strings or `BinaryID` objects are provided.
620
+
621
+ Returns:
622
+ List[viam.proto.app.data.BinaryData]: The binary data.
623
+
624
+ For more information, see `Data Client API <https://docs.viam.com/dev/reference/apis/data-client/#binarydatabyids>`_.
625
+ """
626
+ request = BinaryDataByIDsRequest()
627
+ if len(binary_ids) > 0 and isinstance(binary_ids[0], str):
628
+ binary_data_ids = cast(List[str], binary_ids)
629
+ request = BinaryDataByIDsRequest(binary_data_ids=binary_data_ids, include_binary=True)
630
+ else:
631
+ bin_ids = cast(List[BinaryID], binary_ids)
632
+ request = BinaryDataByIDsRequest(binary_ids=bin_ids, include_binary=True)
633
+ response: BinaryDataByIDsResponse = await self._data_client.BinaryDataByIDs(request, metadata=self._metadata)
634
+ if dest:
635
+ try:
636
+ file = open(dest, "w")
637
+ file.write(f"{response.data}")
638
+ file.flush()
639
+ except Exception as e:
640
+ LOGGER.error(f"Failed to write binary data to file {dest}", exc_info=e)
641
+ return list(response.data)
642
+
643
+ async def delete_tabular_data(self, organization_id: str, delete_older_than_days: int) -> int:
644
+ """Delete tabular data older than a specified number of days.
645
+
646
+ ::
647
+
648
+ tabular_data = await data_client.delete_tabular_data(
649
+ organization_id="<YOUR-ORG-ID>",
650
+ delete_older_than_days=150
651
+ )
652
+
653
+ Args:
654
+ organization_id (str): ID of organization to delete data from.
655
+ You can obtain your organization ID from the Viam app's organization settings page.
656
+ delete_older_than_days (int): Delete data that was captured up to this many days ago. For example if `delete_older_than_days`
657
+ is 10, this deletes any data that was captured up to 10 days ago. If it is 0, all existing data is deleted.
658
+
659
+ Returns:
660
+ int: The number of items deleted.
661
+
662
+ For more information, see `Data Client API <https://docs.viam.com/dev/reference/apis/data-client/#deletetabulardata>`_.
663
+ """
664
+ request = DeleteTabularDataRequest(organization_id=organization_id, delete_older_than_days=delete_older_than_days)
665
+ response: DeleteTabularDataResponse = await self._data_client.DeleteTabularData(request, metadata=self._metadata)
666
+ return response.deleted_count
667
+
668
+ async def delete_tabular_data_by_filter(self, filter: Optional[Filter]) -> int:
669
+ """Deprecated: use delete_tabular_data instead."""
670
+ raise NotImplementedError()
671
+
672
+ async def delete_binary_data_by_filter(self, filter: Optional[Filter]) -> int:
673
+ """Filter and delete binary data.
674
+
675
+ ::
676
+
677
+ from viam.utils import create_filter
678
+
679
+ my_filter = create_filter(component_name="left_motor", organization_ids=["<YOUR-ORG-ID>"])
680
+
681
+ res = await data_client.delete_binary_data_by_filter(my_filter)
682
+
683
+ Args:
684
+ filter (viam.proto.app.data.Filter): Optional `Filter` specifying binary data to delete. Passing an empty `Filter` will lead to
685
+ all data being deleted. Exercise caution when using this option. You must specify an organization ID with
686
+ "organization_ids" when using this option.
687
+
688
+ Returns:
689
+ int: The number of items deleted.
690
+
691
+ For more information, see `Data Client API <https://docs.viam.com/dev/reference/apis/data-client/#deletebinarydatabyfilter>`_.
692
+ """
693
+ filter = filter if filter else Filter()
694
+ request = DeleteBinaryDataByFilterRequest(filter=filter)
695
+ response: DeleteBinaryDataByFilterResponse = await self._data_client.DeleteBinaryDataByFilter(request, metadata=self._metadata)
696
+ return response.deleted_count
697
+
698
+ async def delete_binary_data_by_ids(self, binary_ids: Union[List[BinaryID], List[str]]) -> int:
699
+ """Filter and delete binary data.
700
+
701
+ ::
702
+
703
+ from viam.proto.app.data import BinaryID
704
+ from viam.utils import create_filter
705
+
706
+ my_filter = create_filter(component_name="camera-1", organization_ids=["<YOUR-ORG-ID>"])
707
+ binary_metadata, count, last = await data_client.binary_data_by_filter(
708
+ filter=my_filter,
709
+ limit=20,
710
+ include_binary_data=False
711
+ )
712
+
713
+ my_ids = []
714
+
715
+ for obj in binary_metadata:
716
+ my_ids.append(
717
+ obj.metadata.binary_data_id
718
+ )
719
+
720
+ binary_data = await data_client.delete_binary_data_by_ids(my_ids)
721
+
722
+ Args:
723
+ binary_ids (Union[List[BinaryID], List[str]]): Binary data id strings specifying the data to be deleted or `BinaryID` objects.
724
+ Must be non-empty.
725
+ Note: `BinaryID` objects are deprecated and will be removed in a future release. Please use the binary data id field
726
+ instead.
727
+
728
+ Raises:
729
+ GRPCError: If no binary data id strings or `BinaryID` objects are provided.
730
+
731
+ Returns:
732
+ int: The number of items deleted.
733
+
734
+ For more information, see `Data Client API <https://docs.viam.com/dev/reference/apis/data-client/#deletebinarydatabyids>`_.
735
+ """
736
+ request = DeleteBinaryDataByIDsRequest()
737
+ if len(binary_ids) > 0 and isinstance(binary_ids[0], str):
738
+ binary_data_ids = cast(List[str], binary_ids)
739
+ request = DeleteBinaryDataByIDsRequest(binary_data_ids=binary_data_ids)
740
+ else:
741
+ bin_ids = cast(List[BinaryID], binary_ids)
742
+ request = DeleteBinaryDataByIDsRequest(binary_ids=bin_ids)
743
+ response: DeleteBinaryDataByIDsResponse = await self._data_client.DeleteBinaryDataByIDs(request, metadata=self._metadata)
744
+ return response.deleted_count
745
+
746
+ async def add_tags_to_binary_data_by_ids(self, tags: List[str], binary_ids: Union[List[BinaryID], List[str]]) -> None:
747
+ """Add tags to binary data.
748
+
749
+ ::
750
+
751
+ from viam.utils import create_filter
752
+
753
+ tags = ["tag1", "tag2"]
754
+
755
+ my_filter = create_filter(component_name="camera-1", organization_ids=["<YOUR-ORG-ID>"])
756
+ binary_metadata, count, last = await data_client.binary_data_by_filter(
757
+ filter=my_filter,
758
+ limit=20,
759
+ include_binary_data=False
760
+ )
761
+
762
+ my_ids = []
763
+
764
+ for obj in binary_metadata:
765
+ my_ids.append(
766
+ obj.metadata.binary_data_id
767
+ )
768
+
769
+ binary_data = await data_client.add_tags_to_binary_data_by_ids(tags, my_ids)
770
+
771
+ Args:
772
+ tags (List[str]): List of tags to add to specified binary data. Must be non-empty.
773
+ binary_ids (Union[List[BinaryID], List[str]]): Binary data id strings specifying the data to be tagged or `BinaryID` objects.
774
+ Must be non-empty.
775
+ Note: `BinaryID` objects are deprecated and will be removed in a future release. Please use the binary data id field
776
+ instead.
777
+
778
+ Raises:
779
+ GRPCError: If no binary data id strings or `BinaryID` objects are provided.
780
+
781
+ For more information, see `Data Client API <https://docs.viam.com/dev/reference/apis/data-client/#addtagstobinarydatabyids>`_.
782
+ """
783
+ request = AddTagsToBinaryDataByIDsRequest()
784
+ if len(binary_ids) > 0 and isinstance(binary_ids[0], str):
785
+ binary_data_ids = cast(List[str], binary_ids)
786
+ request = AddTagsToBinaryDataByIDsRequest(binary_data_ids=binary_data_ids, tags=tags)
787
+ else:
788
+ bin_ids = cast(List[BinaryID], binary_ids)
789
+ request = AddTagsToBinaryDataByIDsRequest(binary_ids=bin_ids, tags=tags)
790
+ await self._data_client.AddTagsToBinaryDataByIDs(request, metadata=self._metadata)
791
+
792
+ async def add_tags_to_binary_data_by_filter(self, tags: List[str], filter: Optional[Filter] = None) -> None:
793
+ """Add tags to binary data.
794
+
795
+ ::
796
+
797
+ from viam.utils import create_filter
798
+
799
+ my_filter = create_filter(component_name="my_camera")
800
+ tags = ["tag1", "tag2"]
801
+ await data_client.add_tags_to_binary_data_by_filter(tags, my_filter)
802
+
803
+ Args:
804
+ tags (List[str]): List of tags to add to specified binary data. Must be non-empty.
805
+ filter (viam.proto.app.data.Filter): `Filter` specifying binary data to tag. If no `Filter` is provided, all data will be
806
+ tagged.
807
+
808
+ Raises:
809
+ GRPCError: If no tags are provided.
810
+
811
+ For more information, see `Data Client API <https://docs.viam.com/dev/reference/apis/data-client/#addtagstobinarydatabyfilter>`_.
812
+ """
813
+ filter = filter if filter else Filter()
814
+ request = AddTagsToBinaryDataByFilterRequest(filter=filter, tags=tags)
815
+ await self._data_client.AddTagsToBinaryDataByFilter(request, metadata=self._metadata)
816
+
817
+ async def remove_tags_from_binary_data_by_ids(self, tags: List[str], binary_ids: Union[List[BinaryID], List[str]]) -> int:
818
+ """Remove tags from binary data by IDs.
819
+
820
+ ::
821
+
822
+ from viam.utils import create_filter
823
+
824
+ tags = ["tag1", "tag2"]
825
+
826
+ my_filter = create_filter(component_name="camera-1")
827
+
828
+ binary_metadata, count, last = await data_client.binary_data_by_filter(
829
+ filter=my_filter,
830
+ limit=50,
831
+ include_binary_data=False
832
+ )
833
+
834
+ my_ids = []
835
+
836
+ for obj in binary_metadata:
837
+ my_ids.append(
838
+ obj.metadata.binary_data_id
839
+ )
840
+
841
+ binary_data = await data_client.remove_tags_from_binary_data_by_ids(
842
+ tags, my_ids)
843
+
844
+ Args:
845
+ tags (List[str]): List of tags to remove from specified binary data. Must be non-empty.
846
+ binary_ids (Union[List[BinaryID], List[str]]): Binary data id strings specifying the data to be untagged or `BinaryID` objects.
847
+ Must be non-empty.
848
+ Note: `BinaryID` objects are deprecated and will be removed in a future release. Please use the binary data id field
849
+ instead.
850
+
851
+ Raises:
852
+ GRPCError: If no binary_ids or tags are provided.
853
+
854
+ Returns:
855
+ int: The number of tags removed.
856
+
857
+ For more information, see `Data Client API <https://docs.viam.com/dev/reference/apis/data-client/#removetagsfrombinarydatabyids>`_.
858
+ """
859
+ request = RemoveTagsFromBinaryDataByIDsRequest(tags=tags)
860
+ if len(binary_ids) > 0 and isinstance(binary_ids[0], str):
861
+ binary_data_ids = cast(List[str], binary_ids)
862
+ request = RemoveTagsFromBinaryDataByIDsRequest(binary_data_ids=binary_data_ids, tags=tags)
863
+ else:
864
+ bin_ids = cast(List[BinaryID], binary_ids)
865
+ request = RemoveTagsFromBinaryDataByIDsRequest(binary_ids=bin_ids, tags=tags)
866
+ response: RemoveTagsFromBinaryDataByIDsResponse = await self._data_client.RemoveTagsFromBinaryDataByIDs(
867
+ request, metadata=self._metadata
868
+ )
869
+ return response.deleted_count
870
+
871
+ async def remove_tags_from_binary_data_by_filter(self, tags: List[str], filter: Optional[Filter] = None) -> int:
872
+ """Remove tags from binary data.
873
+
874
+ ::
875
+
876
+ from viam.utils import create_filter
877
+
878
+ my_filter = create_filter(component_name="my_camera")
879
+ tags = ["tag1", "tag2"]
880
+ res = await data_client.remove_tags_from_binary_data_by_filter(tags, my_filter)
881
+
882
+ Args:
883
+ tags (List[str]): List of tags to remove from specified binary data.
884
+ filter (viam.proto.app.data.Filter): `Filter` specifying binary data to untag. If no `Filter` is provided, all data will be
885
+ untagged.
886
+
887
+ Raises:
888
+ GRPCError: If no tags are provided.
889
+
890
+ Returns:
891
+ int: The number of tags removed.
892
+
893
+ For more information, see `Data Client API <https://docs.viam.com/dev/reference/apis/data-client/#removetagsfrombinarydatabyfilter>`_.
894
+ """
895
+ filter = filter if filter else Filter()
896
+ request = RemoveTagsFromBinaryDataByFilterRequest(filter=filter, tags=tags)
897
+ response: RemoveTagsFromBinaryDataByFilterResponse = await self._data_client.RemoveTagsFromBinaryDataByFilter(
898
+ request, metadata=self._metadata
899
+ )
900
+ return response.deleted_count
901
+
902
+ async def tags_by_filter(self, filter: Optional[Filter] = None) -> List[str]:
903
+ """Get a list of tags using a filter.
904
+
905
+ ::
906
+
907
+ from viam.utils import create_filter
908
+
909
+ my_filter = create_filter(component_name="my_camera")
910
+ tags = await data_client.tags_by_filter(my_filter)
911
+
912
+ Args:
913
+ filter (viam.proto.app.data.Filter): `Filter` specifying data to retrieve from. If no `Filter` is provided, all data tags will
914
+ return.
915
+
916
+ Returns:
917
+ List[str]: The list of tags.
918
+
919
+ For more information, see `Data Client API <https://docs.viam.com/dev/reference/apis/data-client/#tagsbyfilter>`_.
920
+ """
921
+ filter = filter if filter else Filter()
922
+ request = TagsByFilterRequest(filter=filter)
923
+ response: TagsByFilterResponse = await self._data_client.TagsByFilter(request, metadata=self._metadata)
924
+ return list(response.tags)
925
+
926
+ async def add_bounding_box_to_image_by_id(
927
+ self,
928
+ binary_id: Union[BinaryID, str],
929
+ label: str,
930
+ x_min_normalized: float,
931
+ y_min_normalized: float,
932
+ x_max_normalized: float,
933
+ y_max_normalized: float,
934
+ ) -> str:
935
+ """Add a bounding box to an image.
936
+
937
+ ::
938
+
939
+ bbox_id = await data_client.add_bounding_box_to_image_by_id(
940
+ binary_id="<YOUR-BINARY-DATA-ID>",
941
+ label="label",
942
+ x_min_normalized=0,
943
+ y_min_normalized=.1,
944
+ x_max_normalized=.2,
945
+ y_max_normalized=.3
946
+ )
947
+
948
+ print(bbox_id)
949
+
950
+ Args:
951
+ binary_id (Union[viam.proto.app.data.BinaryID, str]): The binary data id or `BinaryID` of the image to add the bounding box to.
952
+ Note: `BinaryID` objects are deprecated and will be removed in a future release. Please use the binary data id field
953
+ instead.
954
+ label (str): A label for the bounding box.
955
+ x_min_normalized (float): Min X value of the bounding box normalized from 0 to 1.
956
+ y_min_normalized (float): Min Y value of the bounding box normalized from 0 to 1.
957
+ x_max_normalized (float): Max X value of the bounding box normalized from 0 to 1.
958
+ y_max_normalized (float): Max Y value of the bounding box normalized from 0 to 1.
959
+
960
+ Raises:
961
+ GRPCError: If the X or Y values are outside of the [0, 1] range.
962
+
963
+ Returns:
964
+ str: The bounding box ID.
965
+
966
+ For more information, see `Data Client API <https://docs.viam.com/dev/reference/apis/data-client/#addboundingboxtoimagebyid>`_.
967
+ """
968
+ request = AddBoundingBoxToImageByIDRequest()
969
+ if isinstance(binary_id, str):
970
+ request = AddBoundingBoxToImageByIDRequest(
971
+ binary_data_id=binary_id,
972
+ label=label,
973
+ x_max_normalized=x_max_normalized,
974
+ x_min_normalized=x_min_normalized,
975
+ y_max_normalized=y_max_normalized,
976
+ y_min_normalized=y_min_normalized,
977
+ )
978
+ else:
979
+ request = AddBoundingBoxToImageByIDRequest(
980
+ binary_id=binary_id,
981
+ label=label,
982
+ x_max_normalized=x_max_normalized,
983
+ x_min_normalized=x_min_normalized,
984
+ y_max_normalized=y_max_normalized,
985
+ y_min_normalized=y_min_normalized,
986
+ )
987
+ response: AddBoundingBoxToImageByIDResponse = await self._data_client.AddBoundingBoxToImageByID(request, metadata=self._metadata)
988
+ return response.bbox_id
989
+
990
+ async def remove_bounding_box_from_image_by_id(self, bbox_id: str, binary_id: Union[BinaryID, str]) -> None:
991
+ """Removes a bounding box from an image.
992
+
993
+ ::
994
+
995
+ await data_client.remove_bounding_box_from_image_by_id(
996
+ binary_id="<YOUR-BINARY-DATA-ID>",
997
+ bbox_id="your-bounding-box-id-to-delete"
998
+ )
999
+
1000
+ Args:
1001
+ bbox_id (str): The ID of the bounding box to remove.
1002
+ binary_id (Union[viam.proto.app.data.BinaryID, str]): The binary data id or `BinaryID` of the image to remove the bounding box
1003
+ from.
1004
+ Note: `BinaryID` objects are deprecated and will be removed in a future release. Please use the binary data id field
1005
+ instead.
1006
+
1007
+ For more information, see `Data Client API <https://docs.viam.com/dev/reference/apis/data-client/#removeboundingboxfromimagebyid>`_.
1008
+ """
1009
+ request = RemoveBoundingBoxFromImageByIDRequest()
1010
+ if isinstance(binary_id, str):
1011
+ request = RemoveBoundingBoxFromImageByIDRequest(binary_data_id=binary_id, bbox_id=bbox_id)
1012
+ else:
1013
+ request = RemoveBoundingBoxFromImageByIDRequest(binary_id=binary_id, bbox_id=bbox_id)
1014
+
1015
+ await self._data_client.RemoveBoundingBoxFromImageByID(request, metadata=self._metadata)
1016
+
1017
+ async def bounding_box_labels_by_filter(self, filter: Optional[Filter] = None) -> List[str]:
1018
+ """Get a list of bounding box labels using a `Filter`.
1019
+
1020
+ ::
1021
+
1022
+ from viam.utils import create_filter
1023
+
1024
+ my_filter = create_filter(component_name="my_camera")
1025
+ bounding_box_labels = await data_client.bounding_box_labels_by_filter(
1026
+ my_filter)
1027
+
1028
+ print(bounding_box_labels)
1029
+
1030
+ Args:
1031
+ filter (viam.proto.app.data.Filter): `Filter` specifying data to retrieve from. If no `Filter` is provided, all labels will
1032
+ return.
1033
+
1034
+ Returns:
1035
+ List[str]: The list of bounding box labels.
1036
+
1037
+ For more information, see `Data Client API <https://docs.viam.com/dev/reference/apis/data-client/#boundingboxlabelsbyfilter>`_.
1038
+ """
1039
+ filter = filter if filter else Filter()
1040
+ request = BoundingBoxLabelsByFilterRequest(filter=filter)
1041
+ response: BoundingBoxLabelsByFilterResponse = await self._data_client.BoundingBoxLabelsByFilter(request, metadata=self._metadata)
1042
+ return list(response.labels)
1043
+
1044
+ async def get_database_connection(self, organization_id: str) -> str:
1045
+ """Get a connection to access a MongoDB Atlas Data federation instance.
1046
+
1047
+ ::
1048
+
1049
+ hostname = await data_client.get_database_connection(organization_id="<YOUR-ORG-ID>")
1050
+
1051
+ Args:
1052
+ organization_id (str): Organization to retrieve the connection for.
1053
+ You can obtain your organization ID from the Viam app's organization settings page.
1054
+
1055
+ Returns:
1056
+ str: The hostname of the federated database.
1057
+
1058
+ For more information, see `Data Client API <https://docs.viam.com/dev/reference/apis/data-client/#getdatabaseconnection>`_.
1059
+ """
1060
+ request = GetDatabaseConnectionRequest(organization_id=organization_id)
1061
+ response: GetDatabaseConnectionResponse = await self._data_client.GetDatabaseConnection(request, metadata=self._metadata)
1062
+ return response.hostname
1063
+
1064
+ async def configure_database_user(self, organization_id: str, password: str) -> None:
1065
+ """Configure a database user for the Viam organization's MongoDB Atlas Data Federation instance. It can also be used to reset the
1066
+ password of the existing database user.
1067
+
1068
+ ::
1069
+
1070
+ await data_client.configure_database_user(
1071
+ organization_id="<YOUR-ORG-ID>",
1072
+ password="Your_Password@1234"
1073
+ )
1074
+
1075
+ Args:
1076
+ organization_id (str): The ID of the organization.
1077
+ You can obtain your organization ID from the Viam app's organization settings page.
1078
+ password (str): The password of the user.
1079
+
1080
+ For more information, see `Data Client API <https://docs.viam.com/dev/reference/apis/data-client/#configuredatabaseuser>`_.
1081
+ """
1082
+ request = ConfigureDatabaseUserRequest(organization_id=organization_id, password=password)
1083
+ await self._data_client.ConfigureDatabaseUser(request, metadata=self._metadata)
1084
+
1085
+ async def create_dataset(self, name: str, organization_id: str) -> str:
1086
+ """Create a new dataset.
1087
+
1088
+ ::
1089
+
1090
+ dataset_id = await data_client.create_dataset(
1091
+ name="<DATASET-NAME>",
1092
+ organization_id="<YOUR-ORG-ID>"
1093
+ )
1094
+ print(dataset_id)
1095
+
1096
+ Args:
1097
+ name (str): The name of the dataset being created.
1098
+ organization_id (str): The ID of the organization where the dataset is being created.
1099
+ You can obtain your organization ID from the Viam app's organization settings page.
1100
+
1101
+ Returns:
1102
+ str: The dataset ID of the created dataset.
1103
+
1104
+ For more information, see `Data Client API <https://docs.viam.com/dev/reference/apis/data-client/#createdataset>`_.
1105
+ """
1106
+ request = CreateDatasetRequest(name=name, organization_id=organization_id)
1107
+ response: CreateDatasetResponse = await self._dataset_client.CreateDataset(request, metadata=self._metadata)
1108
+ return response.id
1109
+
1110
+ async def list_dataset_by_ids(self, ids: List[str]) -> Sequence[Dataset]:
1111
+ """Get a list of datasets using their IDs.
1112
+
1113
+ ::
1114
+
1115
+ datasets = await data_client.list_dataset_by_ids(
1116
+ ids=["<YOUR-DATASET-ID-1>, <YOUR-DATASET-ID-2>"]
1117
+ )
1118
+ print(datasets)
1119
+
1120
+ Args:
1121
+ ids (List[str]): The IDs of the datasets being called for. To retrieve these IDs,
1122
+ navigate to your dataset's page in the Viam app,
1123
+ click **...** in the left-hand menu, and click **Copy dataset ID**.
1124
+
1125
+ Returns:
1126
+ Sequence[Dataset]: The list of datasets.
1127
+
1128
+ For more information, see `Data Client API <https://docs.viam.com/dev/reference/apis/data-client/#listdatasetsbyids>`_.
1129
+ """
1130
+ request = ListDatasetsByIDsRequest(ids=ids)
1131
+ response: ListDatasetsByIDsResponse = await self._dataset_client.ListDatasetsByIDs(request, metadata=self._metadata)
1132
+
1133
+ return response.datasets
1134
+
1135
+ async def list_datasets_by_organization_id(self, organization_id: str) -> Sequence[Dataset]:
1136
+ """Get the datasets in an organization.
1137
+
1138
+ ::
1139
+
1140
+ datasets = await data_client.list_datasets_by_organization_id(
1141
+ organization_id="<YOUR-ORG-ID>"
1142
+ )
1143
+ print(datasets)
1144
+
1145
+ Args:
1146
+ organization_id (str): The ID of the organization.
1147
+ You can obtain your organization ID from the Viam app's organization settings page.
1148
+
1149
+ Returns:
1150
+ Sequence[Dataset]: The list of datasets in the organization.
1151
+
1152
+ For more information, see `Data Client API <https://docs.viam.com/dev/reference/apis/data-client/#listdatasetsbyorganizationid>`_.
1153
+ """
1154
+ request = ListDatasetsByOrganizationIDRequest(organization_id=organization_id)
1155
+ response: ListDatasetsByOrganizationIDResponse = await self._dataset_client.ListDatasetsByOrganizationID(
1156
+ request, metadata=self._metadata
1157
+ )
1158
+
1159
+ return response.datasets
1160
+
1161
+ async def rename_dataset(self, id: str, name: str) -> None:
1162
+ """Rename a dataset specified by the dataset ID.
1163
+
1164
+ ::
1165
+
1166
+ await data_client.rename_dataset(
1167
+ id="<YOUR-DATASET-ID>",
1168
+ name="MyDataset"
1169
+ )
1170
+
1171
+ Args:
1172
+ id (str): The ID of the dataset. You can retrieve this by navigating to the **DATASETS** sub-tab of the **DATA** tab,
1173
+ clicking on the dataset, clicking the **...** menu and selecting **Copy dataset ID**.
1174
+ name (str): The new name of the dataset.
1175
+
1176
+ For more information, see `Data Client API <https://docs.viam.com/dev/reference/apis/data-client/#renamedataset>`_.
1177
+ """
1178
+ request = RenameDatasetRequest(id=id, name=name)
1179
+ await self._dataset_client.RenameDataset(request, metadata=self._metadata)
1180
+
1181
+ async def delete_dataset(self, id: str) -> None:
1182
+ """Delete a dataset.
1183
+
1184
+ ::
1185
+
1186
+ await data_client.delete_dataset(
1187
+ id="<YOUR-DATASET-ID>"
1188
+ )
1189
+
1190
+ Args:
1191
+ id (str): The ID of the dataset. You can retrieve this by navigating to the **DATASETS** sub-tab of the **DATA** tab,
1192
+ clicking on the dataset, clicking the **...** menu and selecting **Copy dataset ID**.
1193
+
1194
+ For more information, see `Data Client API <https://docs.viam.com/dev/reference/apis/data-client/#deletedataset>`_.
1195
+ """
1196
+ request = DeleteDatasetRequest(id=id)
1197
+ await self._dataset_client.DeleteDataset(request, metadata=self._metadata)
1198
+
1199
+ async def add_binary_data_to_dataset_by_ids(self, binary_ids: Union[List[BinaryID], List[str]], dataset_id: str) -> None:
1200
+ """Add the BinaryData to the provided dataset.
1201
+
1202
+ This BinaryData will be tagged with the VIAM_DATASET_{id} label.
1203
+
1204
+ ::
1205
+
1206
+ binary_metadata, count, last = await data_client.binary_data_by_filter(
1207
+ include_binary_data=False
1208
+ )
1209
+
1210
+ my_binary_ids = []
1211
+
1212
+ for obj in binary_metadata:
1213
+ my_binary_ids.append(
1214
+ obj.metadata.binary_data_id
1215
+ )
1216
+
1217
+ await data_client.add_binary_data_to_dataset_by_ids(
1218
+ binary_ids=my_binary_ids,
1219
+ dataset_id="abcd-1234xyz-8765z-123abc"
1220
+ )
1221
+
1222
+ Args:
1223
+ binary_ids (List[BinaryID]): The IDs of binary data to add to dataset. To retrieve these IDs,
1224
+ navigate to your data page, click on an image and copy its File ID from the details tab.
1225
+ To retrieve the dataset ID, navigate to your dataset's page in the Viam app,
1226
+ and use the left-hand menu to copy the dataset ID.
1227
+ dataset_id (str): The ID of the dataset to be added to.
1228
+
1229
+ For more information, see `Data Client API <https://docs.viam.com/dev/reference/apis/data-client/#addbinarydatatodatasetbyids>`_.
1230
+ """
1231
+ request = AddBinaryDataToDatasetByIDsRequest()
1232
+ if len(binary_ids) > 0 and isinstance(binary_ids[0], str):
1233
+ binary_data_ids = cast(List[str], binary_ids)
1234
+ request = AddBinaryDataToDatasetByIDsRequest(binary_data_ids=binary_data_ids, dataset_id=dataset_id)
1235
+ else:
1236
+ bin_ids = cast(List[BinaryID], binary_ids)
1237
+ request = AddBinaryDataToDatasetByIDsRequest(binary_ids=bin_ids, dataset_id=dataset_id)
1238
+ await self._data_client.AddBinaryDataToDatasetByIDs(request, metadata=self._metadata)
1239
+
1240
+ async def remove_binary_data_from_dataset_by_ids(self, binary_ids: Union[List[BinaryID], List[str]], dataset_id: str) -> None:
1241
+ """Remove the BinaryData from the provided dataset.
1242
+
1243
+ This BinaryData will lose the VIAM_DATASET_{id} tag.
1244
+
1245
+ ::
1246
+
1247
+ binary_metadata, count, last = await data_client.binary_data_by_filter(
1248
+ include_binary_data=False
1249
+ )
1250
+
1251
+ my_binary_ids = []
1252
+
1253
+ for obj in binary_metadata:
1254
+ my_binary_ids.append(
1255
+ obj.metadata.binary_data_id
1256
+ )
1257
+
1258
+ await data_client.remove_binary_data_from_dataset_by_ids(
1259
+ binary_ids=my_binary_ids,
1260
+ dataset_id="abcd-1234xyz-8765z-123abc"
1261
+ )
1262
+
1263
+ Args:
1264
+ binary_ids (Union[List[BinaryID], List[str]]): The IDs of binary data to remove from dataset. To retrieve these IDs,
1265
+ navigate to your data page, click on an image and copy its File ID from the details tab. To
1266
+ retrieve the dataset ID, navigate to your dataset's page in the Viam app, and use the
1267
+ left-hand menu to copy the dataset ID.
1268
+ Note: `BinaryID` objects are deprecated and will be removed in a future release. Please use the binary data id field
1269
+ instead.
1270
+ dataset_id (str): The ID of the dataset to be removed from.
1271
+
1272
+ For more information, see `Data Client API <https://docs.viam.com/dev/reference/apis/data-client/#removebinarydatafromdatasetbyids>`_.
1273
+ """
1274
+ request = RemoveBinaryDataFromDatasetByIDsRequest()
1275
+ if len(binary_ids) > 0 and isinstance(binary_ids[0], str):
1276
+ binary_data_ids = cast(List[str], binary_ids)
1277
+ request = RemoveBinaryDataFromDatasetByIDsRequest(binary_data_ids=binary_data_ids, dataset_id=dataset_id)
1278
+ else:
1279
+ bin_ids = cast(List[BinaryID], binary_ids)
1280
+ request = RemoveBinaryDataFromDatasetByIDsRequest(binary_ids=bin_ids, dataset_id=dataset_id)
1281
+ await self._data_client.RemoveBinaryDataFromDatasetByIDs(request, metadata=self._metadata)
1282
+
1283
+ async def binary_data_capture_upload(
1284
+ self,
1285
+ binary_data: bytes,
1286
+ part_id: str,
1287
+ component_type: str,
1288
+ component_name: str,
1289
+ method_name: str,
1290
+ file_extension: str,
1291
+ method_parameters: Optional[Mapping[str, Any]] = None,
1292
+ tags: Optional[List[str]] = None,
1293
+ data_request_times: Optional[Tuple[datetime, datetime]] = None,
1294
+ ) -> str:
1295
+ """Upload binary sensor data.
1296
+
1297
+ Upload binary data collected on a robot through a specific component (for example, a motor) along with the relevant metadata to
1298
+ app.viam.com. Binary data can be found under the "Files" subtab of the Data tab on app.viam.com.
1299
+
1300
+ ::
1301
+
1302
+ time_requested = datetime(2023, 6, 5, 11)
1303
+ time_received = datetime(2023, 6, 5, 11, 0, 3)
1304
+
1305
+ file_id = await data_client.binary_data_capture_upload(
1306
+ part_id="INSERT YOUR PART ID",
1307
+ component_type='camera',
1308
+ component_name='my_camera',
1309
+ method_name='GetImages',
1310
+ method_parameters=None,
1311
+ tags=["tag_1", "tag_2"],
1312
+ data_request_times=[time_requested, time_received],
1313
+ file_extension=".jpg",
1314
+ binary_data=b"Encoded image bytes"
1315
+ )
1316
+
1317
+ Args:
1318
+ binary_data (bytes): The data to be uploaded, represented in bytes.
1319
+ part_id (str): Part ID of the component used to capture the data.
1320
+ component_type (str): Type of the component used to capture the data (for example, "movement_sensor").
1321
+ component_name (str): Name of the component used to capture the data.
1322
+ method_name (str): Name of the method used to capture the data.
1323
+ file_extension (str): The file extension of binary data including the period, for example .jpg, .png, .pcd.
1324
+ The backend will route the binary to its corresponding mime type based on this extension. Files with a .jpeg, .jpg,
1325
+ or .png extension will be saved to the images tab.
1326
+ method_parameters (Optional[Mapping[str, Any]]): Optional dictionary of method parameters. No longer in active use.
1327
+ tags (Optional[List[str]]): Optional list of tags to allow for tag-based data filtering when retrieving data.
1328
+ data_request_times (Optional[Tuple[datetime.datetime, datetime.datetime]]): Optional tuple containing datetime objects
1329
+ denoting the times this data was requested[0] by the robot and received[1] from the appropriate sensor.
1330
+
1331
+ Raises:
1332
+ GRPCError: If an invalid part ID is passed.
1333
+
1334
+ Returns:
1335
+ str: The binary_data_id of the uploaded data.
1336
+
1337
+ For more information, see `Data Client API <https://docs.viam.com/dev/reference/apis/data-client/#binarydatacaptureupload>`_.
1338
+ """
1339
+ sensor_contents = SensorData(
1340
+ metadata=(
1341
+ SensorMetadata(
1342
+ time_requested=datetime_to_timestamp(data_request_times[0]) if data_request_times else None,
1343
+ time_received=datetime_to_timestamp(data_request_times[1]) if data_request_times else None,
1344
+ )
1345
+ if data_request_times
1346
+ else None
1347
+ ),
1348
+ struct=None, # Used for tabular data.
1349
+ binary=binary_data,
1350
+ )
1351
+ metadata = UploadMetadata(
1352
+ part_id=part_id,
1353
+ component_type=component_type,
1354
+ component_name=component_name,
1355
+ method_name=method_name,
1356
+ type=DataType.DATA_TYPE_BINARY_SENSOR,
1357
+ method_parameters=method_parameters,
1358
+ tags=tags,
1359
+ )
1360
+ if file_extension:
1361
+ metadata.file_extension = file_extension if file_extension[0] == "." else f".{file_extension}"
1362
+ response = await self._data_capture_upload(metadata=metadata, sensor_contents=[sensor_contents])
1363
+ return response.binary_data_id
1364
+
1365
+ async def tabular_data_capture_upload(
1366
+ self,
1367
+ tabular_data: List[Mapping[str, Any]],
1368
+ part_id: str,
1369
+ component_type: str,
1370
+ component_name: str,
1371
+ method_name: str,
1372
+ data_request_times: List[Tuple[datetime, datetime]],
1373
+ method_parameters: Optional[Mapping[str, Any]] = None,
1374
+ tags: Optional[List[str]] = None,
1375
+ ) -> str:
1376
+ """Upload tabular sensor data.
1377
+
1378
+ Upload tabular data collected on a robot through a specific component (for example, a motor) along with the relevant metadata to
1379
+ app.viam.com. Tabular data can be found under the "Sensors" subtab of the Data tab on app.viam.com.
1380
+
1381
+ ::
1382
+
1383
+ from datetime import datetime
1384
+
1385
+ time_requested = datetime(2023, 6, 5, 11)
1386
+ time_received = datetime(2023, 6, 5, 11, 0, 3)
1387
+ file_id = await data_client.tabular_data_capture_upload(
1388
+ part_id="INSERT YOUR PART ID",
1389
+ component_type='rdk:component:movement_sensor',
1390
+ component_name='my_movement_sensor',
1391
+ method_name='Readings',
1392
+ tags=["sensor_data"],
1393
+ data_request_times=[(time_requested, time_received)],
1394
+ tabular_data=[{
1395
+ 'readings': {
1396
+ 'linear_velocity': {'x': 0.5, 'y': 0.0, 'z': 0.0},
1397
+ 'angular_velocity': {'x': 0.0, 'y': 0.0, 'z': 0.1}
1398
+ }
1399
+ }]
1400
+ )
1401
+
1402
+ Args:
1403
+ tabular_data (List[Mapping[str, Any]]): List of the data to be uploaded, represented tabularly as a collection of dictionaries.
1404
+ Must include the key "readings" for sensors.
1405
+ part_id (str): Part ID of the component used to capture the data.
1406
+ component_type (str): Type of the component used to capture the data (for example, "rdk:component:movement_sensor").
1407
+ component_name (str): Name of the component used to capture the data.
1408
+ method_name (str): Name of the method used to capture the data.
1409
+ data_request_times (List[Tuple[datetime.datetime, datetime.datetime]]): List of tuples, each containing `datetime` objects
1410
+ denoting the times this data was requested[0] by the robot and received[1] from the appropriate sensor. Passing a list of
1411
+ tabular data and Timestamps with length n > 1 will result in n datapoints being uploaded, all tied to the same metadata.
1412
+ method_parameters (Optional[Mapping[str, Any]]): Optional dictionary of method parameters. No longer in active use.
1413
+ tags (Optional[List[str]]): Optional list of tags to allow for tag-based data filtering when retrieving data.
1414
+
1415
+ Raises:
1416
+ GRPCError: If an invalid part ID is passed.
1417
+ ValueError: If a list of `Timestamp` objects is provided and its length does not match the length of the list of tabular
1418
+ data.
1419
+
1420
+ Returns:
1421
+ str: The file_id of the uploaded data.
1422
+
1423
+ For more information, see `Data Client API <https://docs.viam.com/dev/reference/apis/data-client/#tabulardatacaptureupload>`_.
1424
+ """
1425
+ sensor_contents = []
1426
+ if len(data_request_times) != len(tabular_data):
1427
+ raise ValueError("data_request_times and tabular_data lengths must be equal.")
1428
+
1429
+ for idx, tab in enumerate(tabular_data):
1430
+ s = Struct()
1431
+ s.update(tab)
1432
+ sensor_contents.append(
1433
+ SensorData(
1434
+ metadata=(
1435
+ SensorMetadata(
1436
+ time_requested=datetime_to_timestamp(data_request_times[idx][0]) if data_request_times else None,
1437
+ time_received=datetime_to_timestamp(data_request_times[idx][1]) if data_request_times else None,
1438
+ )
1439
+ if data_request_times[idx]
1440
+ else None
1441
+ )
1442
+ if data_request_times
1443
+ else None,
1444
+ struct=s,
1445
+ )
1446
+ )
1447
+
1448
+ metadata = UploadMetadata(
1449
+ part_id=part_id,
1450
+ component_type=component_type,
1451
+ component_name=component_name,
1452
+ method_name=method_name,
1453
+ type=DataType.DATA_TYPE_TABULAR_SENSOR,
1454
+ method_parameters=method_parameters,
1455
+ tags=tags,
1456
+ )
1457
+ response = await self._data_capture_upload(metadata=metadata, sensor_contents=sensor_contents)
1458
+ return response.file_id
1459
+
1460
+ async def _data_capture_upload(self, metadata: UploadMetadata, sensor_contents: List[SensorData]) -> DataCaptureUploadResponse:
1461
+ request = DataCaptureUploadRequest(metadata=metadata, sensor_contents=sensor_contents)
1462
+ response: DataCaptureUploadResponse = await self._data_sync_client.DataCaptureUpload(request, metadata=self._metadata)
1463
+ return response
1464
+
1465
+ async def streaming_data_capture_upload(
1466
+ self,
1467
+ data: bytes,
1468
+ part_id: str,
1469
+ file_ext: str,
1470
+ component_type: Optional[str] = None,
1471
+ component_name: Optional[str] = None,
1472
+ method_name: Optional[str] = None,
1473
+ method_parameters: Optional[Mapping[str, Any]] = None,
1474
+ data_request_times: Optional[Tuple[datetime, datetime]] = None,
1475
+ tags: Optional[List[str]] = None,
1476
+ ) -> str:
1477
+ """Uploads the metadata and contents of streaming binary data.
1478
+
1479
+ ::
1480
+
1481
+ time_requested = datetime(2023, 6, 5, 11)
1482
+ time_received = datetime(2023, 6, 5, 11, 0, 3)
1483
+
1484
+ file_id = await data_client.streaming_data_capture_upload(
1485
+ data="byte-data-to-upload",
1486
+ part_id="INSERT YOUR PART ID",
1487
+ file_ext="png",
1488
+ component_type='motor',
1489
+ component_name='left_motor',
1490
+ method_name='IsPowered',
1491
+ data_request_times=[time_requested, time_received],
1492
+ tags=["tag_1", "tag_2"]
1493
+ )
1494
+
1495
+ Args:
1496
+ data (bytes): the data to be uploaded.
1497
+ part_id (str): Part ID of the resource associated with the file.
1498
+ file_ext (str): file extension type for the data. required for determining MIME type.
1499
+ component_type (Optional[str]): Optional type of the component associated with the file (for example, "movement_sensor").
1500
+ component_name (Optional[str]): Optional name of the component associated with the file.
1501
+ method_name (Optional[str]): Optional name of the method associated with the file.
1502
+ method_parameters (Optional[str]): Optional dictionary of the method parameters. No longer in active use.
1503
+ data_request_times (Optional[Tuple[datetime.datetime, datetime.datetime]]): Optional tuple containing datetime objects
1504
+ denoting the times this data was requested[0] by the robot and received[1] from the appropriate sensor.
1505
+ tags (Optional[List[str]]): Optional list of tags to allow for tag-based filtering when retrieving data.
1506
+
1507
+ Raises:
1508
+ GRPCError: If an invalid part ID is passed.
1509
+
1510
+ Returns:
1511
+ str: The binary_data_id of the uploaded data.
1512
+
1513
+ For more information, see `Data Client API <https://docs.viam.com/dev/reference/apis/data-client/#streamingdatacaptureupload>`_.
1514
+ """
1515
+
1516
+ upload_metadata = UploadMetadata(
1517
+ part_id=part_id,
1518
+ component_type=component_type if component_type else "",
1519
+ component_name=component_name if component_name else "",
1520
+ method_name=method_name if method_name else "",
1521
+ method_parameters=method_parameters,
1522
+ type=DataType.DATA_TYPE_BINARY_SENSOR,
1523
+ file_extension=file_ext if file_ext[0] == "." else f".{file_ext}",
1524
+ tags=tags,
1525
+ )
1526
+ sensor_metadata = SensorMetadata(
1527
+ time_requested=datetime_to_timestamp(data_request_times[0]) if data_request_times else None,
1528
+ time_received=datetime_to_timestamp(data_request_times[1]) if data_request_times else None,
1529
+ )
1530
+ metadata = DataCaptureUploadMetadata(upload_metadata=upload_metadata, sensor_metadata=sensor_metadata)
1531
+ request_metadata = StreamingDataCaptureUploadRequest(metadata=metadata)
1532
+ stream: Stream[StreamingDataCaptureUploadRequest, StreamingDataCaptureUploadResponse]
1533
+ async with self._data_sync_client.StreamingDataCaptureUpload.open(metadata=self._metadata) as stream:
1534
+ await stream.send_message(request_metadata)
1535
+ await stream.send_message(StreamingDataCaptureUploadRequest(data=data), end=True)
1536
+ response = await stream.recv_message()
1537
+ if not response:
1538
+ await stream.recv_trailing_metadata() # causes us to throw appropriate gRPC error
1539
+ raise TypeError("Response cannot be empty")
1540
+ return response.binary_data_id
1541
+
1542
+ async def file_upload(
1543
+ self,
1544
+ part_id: str,
1545
+ data: bytes,
1546
+ component_type: Optional[str] = None,
1547
+ component_name: Optional[str] = None,
1548
+ method_name: Optional[str] = None,
1549
+ file_name: Optional[str] = None,
1550
+ method_parameters: Optional[Mapping[str, Any]] = None,
1551
+ file_extension: Optional[str] = None,
1552
+ tags: Optional[List[str]] = None,
1553
+ ) -> str:
1554
+ """Upload arbitrary file data.
1555
+
1556
+ Upload file data that may be stored on a robot along with the relevant metadata to app.viam.com. File data can be found under the
1557
+ "Files" subtab of the Data tab on app.viam.com.
1558
+
1559
+ ::
1560
+
1561
+ file_id = await data_client.file_upload(
1562
+ data=b"Encoded image bytes",
1563
+ part_id="INSERT YOUR PART ID",
1564
+ tags=["tag_1", "tag_2"],
1565
+ file_name="your-file",
1566
+ file_extension=".txt"
1567
+ )
1568
+
1569
+ Args:
1570
+ part_id (str): Part ID of the resource associated with the file.
1571
+ data (bytes): Bytes representing file data to upload.
1572
+ component_type (Optional[str]): Optional type of the component associated with the file (for example, "movement_sensor").
1573
+ component_name (Optional[str]): Optional name of the component associated with the file.
1574
+ method_name (Optional[str]): Optional name of the method associated with the file.
1575
+ file_name (Optional[str]): Optional name of the file. The empty string "" will be assigned as the file name if one isn't
1576
+ provided.
1577
+ method_parameters (Optional[str]): Optional dictionary of the method parameters. No longer in active use.
1578
+ file_extension (Optional[str]): Optional file extension. The empty string "" will be assigned as the file extension if one isn't
1579
+ provided. Files with a .jpeg, .jpg, or .png extension will be saved to the images tab.
1580
+ tags (Optional[List[str]]): Optional list of tags to allow for tag-based filtering when retrieving data.
1581
+
1582
+ Raises:
1583
+ GRPCError: If an invalid part ID is passed.
1584
+
1585
+ Returns:
1586
+ str: ID of the new file.
1587
+
1588
+ For more information, see `Data Client API <https://docs.viam.com/dev/reference/apis/data-client/#fileupload>`_.
1589
+ """
1590
+ metadata = UploadMetadata(
1591
+ part_id=part_id,
1592
+ component_type=component_type if component_type else "",
1593
+ component_name=component_name if component_name else "",
1594
+ method_name=method_name if method_name else "",
1595
+ type=DataType.DATA_TYPE_FILE,
1596
+ file_name=file_name if file_name else "",
1597
+ method_parameters=method_parameters,
1598
+ file_extension=file_extension if file_extension else "",
1599
+ tags=tags,
1600
+ )
1601
+ response: FileUploadResponse = await self._file_upload(metadata=metadata, file_contents=FileData(data=data))
1602
+ return response.binary_data_id
1603
+
1604
+ async def file_upload_from_path(
1605
+ self,
1606
+ filepath: str,
1607
+ part_id: str,
1608
+ component_type: Optional[str] = None,
1609
+ component_name: Optional[str] = None,
1610
+ method_name: Optional[str] = None,
1611
+ method_parameters: Optional[Mapping[str, Any]] = None,
1612
+ tags: Optional[List[str]] = None,
1613
+ ) -> str:
1614
+ """Upload arbitrary file data.
1615
+
1616
+ Upload file data that may be stored on a robot along with the relevant metadata to app.viam.com. File data can be found under the
1617
+ "Files" subtab of the Data tab on app.viam.com.
1618
+
1619
+ ::
1620
+
1621
+ file_id = await data_client.file_upload_from_path(
1622
+ part_id="INSERT YOUR PART ID",
1623
+ tags=["tag_1", "tag_2"],
1624
+ filepath="/Users/<your-username>/<your-directory>/<your-file.txt>"
1625
+ )
1626
+
1627
+ Args:
1628
+ filepath (str): Absolute filepath of file to be uploaded.
1629
+ part_id (str): Part ID of the component associated with the file.
1630
+ component_type (Optional[str]): Optional type of the component associated with the file (for example, "movement_sensor").
1631
+ component_name (Optional[str]): Optional name of the component associated with the file.
1632
+ method_name (Optional[str]): Optional name of the method associated with the file.
1633
+ method_parameters (Optional[str]): Optional dictionary of the method parameters. No longer in active use.
1634
+ tags (Optional[List[str]]): Optional list of tags to allow for tag-based filtering when retrieving data.
1635
+
1636
+
1637
+ Raises:
1638
+ GRPCError: If an invalid part ID is passed.
1639
+ FileNotFoundError: If the provided filepath is not found.
1640
+
1641
+ Returns:
1642
+ str: ID of the new file.
1643
+
1644
+ For more information, see `Data Client API <https://docs.viam.com/dev/reference/apis/data-client/#fileuploadfrompath>`_.
1645
+ """
1646
+ path = Path(filepath)
1647
+ file_name = path.stem
1648
+ file_extension = path.suffix if path.suffix != "" else None
1649
+ f = open(filepath, "rb")
1650
+ data = f.read()
1651
+ f.close()
1652
+
1653
+ metadata = UploadMetadata(
1654
+ part_id=part_id,
1655
+ component_type=component_type if component_type else "",
1656
+ component_name=component_name if component_name else "",
1657
+ method_name=method_name if method_name else "",
1658
+ type=DataType.DATA_TYPE_FILE,
1659
+ file_name=file_name,
1660
+ method_parameters=method_parameters,
1661
+ file_extension=file_extension if file_extension else "",
1662
+ tags=tags,
1663
+ )
1664
+ response: FileUploadResponse = await self._file_upload(metadata=metadata, file_contents=FileData(data=data if data else bytes()))
1665
+ return response.binary_data_id
1666
+
1667
+ async def _file_upload(self, metadata: UploadMetadata, file_contents: FileData) -> FileUploadResponse:
1668
+ request_metadata = FileUploadRequest(metadata=metadata)
1669
+ request_file_contents = FileUploadRequest(file_contents=file_contents)
1670
+ stream: Stream[FileUploadRequest, FileUploadResponse]
1671
+ async with self._data_sync_client.FileUpload.open(metadata=self._metadata) as stream:
1672
+ await stream.send_message(request_metadata)
1673
+ await stream.send_message(request_file_contents, end=True)
1674
+ response = await stream.recv_message()
1675
+ if not response:
1676
+ await stream.recv_trailing_metadata() # causes us to throw appropriate gRPC error.
1677
+ raise TypeError("Response cannot be empty")
1678
+ return response
1679
+
1680
+ @staticmethod
1681
+ def create_filter(
1682
+ component_name: Optional[str] = None,
1683
+ component_type: Optional[str] = None,
1684
+ method: Optional[str] = None,
1685
+ robot_name: Optional[str] = None,
1686
+ robot_id: Optional[str] = None,
1687
+ part_name: Optional[str] = None,
1688
+ part_id: Optional[str] = None,
1689
+ location_ids: Optional[List[str]] = None,
1690
+ organization_ids: Optional[List[str]] = None,
1691
+ mime_type: Optional[List[str]] = None,
1692
+ start_time: Optional[datetime] = None,
1693
+ end_time: Optional[datetime] = None,
1694
+ tags: Optional[List[str]] = None,
1695
+ bbox_labels: Optional[List[str]] = None,
1696
+ dataset_id: Optional[str] = None,
1697
+ ) -> Filter:
1698
+ warnings.warn("DataClient.create_filter is deprecated. Use utils.create_filter instead.", DeprecationWarning, stacklevel=2)
1699
+ return create_filter(
1700
+ component_name,
1701
+ component_type,
1702
+ method,
1703
+ robot_name,
1704
+ robot_id,
1705
+ part_name,
1706
+ part_id,
1707
+ location_ids,
1708
+ organization_ids,
1709
+ mime_type,
1710
+ start_time,
1711
+ end_time,
1712
+ tags,
1713
+ bbox_labels,
1714
+ dataset_id,
1715
+ )