viam-sdk 0.25.2__py3-none-linux_armv7l.whl → 0.62.0__py3-none-linux_armv7l.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.
Files changed (328) hide show
  1. viam/app/_logs.py +3 -6
  2. viam/app/app_client.py +606 -262
  3. viam/app/billing_client.py +60 -18
  4. viam/app/data_client.py +1086 -315
  5. viam/app/ml_training_client.py +51 -48
  6. viam/app/provisioning_client.py +3 -5
  7. viam/app/viam_client.py +105 -11
  8. viam/components/arm/__init__.py +1 -25
  9. viam/components/arm/arm.py +21 -22
  10. viam/components/arm/client.py +27 -30
  11. viam/components/arm/service.py +3 -3
  12. viam/components/audio_in/__init__.py +24 -0
  13. viam/components/audio_in/audio_in.py +74 -0
  14. viam/components/audio_in/client.py +76 -0
  15. viam/components/audio_in/service.py +83 -0
  16. viam/components/audio_input/__init__.py +1 -1
  17. viam/components/audio_input/audio_input.py +4 -3
  18. viam/components/audio_input/client.py +19 -8
  19. viam/components/audio_input/service.py +10 -0
  20. viam/components/audio_out/__init__.py +21 -0
  21. viam/components/audio_out/audio_out.py +72 -0
  22. viam/components/audio_out/client.py +67 -0
  23. viam/components/audio_out/service.py +63 -0
  24. viam/components/base/__init__.py +2 -10
  25. viam/components/base/base.py +20 -20
  26. viam/components/base/client.py +27 -30
  27. viam/components/board/__init__.py +2 -25
  28. viam/components/board/board.py +39 -77
  29. viam/components/board/client.py +39 -73
  30. viam/components/button/__init__.py +10 -0
  31. viam/components/button/button.py +41 -0
  32. viam/components/button/client.py +52 -0
  33. viam/components/button/service.py +46 -0
  34. viam/components/camera/__init__.py +1 -1
  35. viam/components/camera/camera.py +31 -22
  36. viam/components/camera/client.py +30 -20
  37. viam/components/camera/service.py +14 -12
  38. viam/components/component_base.py +10 -7
  39. viam/components/encoder/__init__.py +1 -1
  40. viam/components/encoder/client.py +15 -16
  41. viam/components/encoder/encoder.py +9 -9
  42. viam/components/gantry/__init__.py +1 -13
  43. viam/components/gantry/client.py +41 -28
  44. viam/components/gantry/gantry.py +48 -17
  45. viam/components/gantry/service.py +21 -5
  46. viam/components/generic/__init__.py +1 -1
  47. viam/components/generic/client.py +11 -7
  48. viam/components/generic/generic.py +3 -3
  49. viam/components/gripper/__init__.py +3 -12
  50. viam/components/gripper/client.py +43 -20
  51. viam/components/gripper/gripper.py +87 -12
  52. viam/components/gripper/service.py +32 -3
  53. viam/components/input/__init__.py +1 -14
  54. viam/components/input/client.py +22 -23
  55. viam/components/input/input.py +18 -12
  56. viam/components/motor/__init__.py +1 -21
  57. viam/components/motor/client.py +36 -42
  58. viam/components/motor/motor.py +24 -24
  59. viam/components/movement_sensor/__init__.py +1 -1
  60. viam/components/movement_sensor/client.py +33 -40
  61. viam/components/movement_sensor/movement_sensor.py +12 -12
  62. viam/components/pose_tracker/__init__.py +1 -1
  63. viam/components/pose_tracker/client.py +9 -8
  64. viam/components/pose_tracker/pose_tracker.py +2 -2
  65. viam/components/power_sensor/__init__.py +1 -1
  66. viam/components/power_sensor/client.py +15 -18
  67. viam/components/power_sensor/power_sensor.py +12 -12
  68. viam/components/sensor/__init__.py +1 -1
  69. viam/components/sensor/client.py +9 -8
  70. viam/components/sensor/sensor.py +5 -5
  71. viam/components/servo/__init__.py +1 -13
  72. viam/components/servo/client.py +18 -18
  73. viam/components/servo/servo.py +12 -12
  74. viam/components/switch/__init__.py +10 -0
  75. viam/components/switch/client.py +83 -0
  76. viam/components/switch/service.py +72 -0
  77. viam/components/switch/switch.py +95 -0
  78. viam/gen/app/agent/v1/agent_pb2.py +40 -29
  79. viam/gen/app/agent/v1/agent_pb2.pyi +73 -11
  80. viam/gen/app/cloudslam/v1/cloud_slam_pb2.py +45 -42
  81. viam/gen/app/data/v1/data_grpc.py +98 -2
  82. viam/gen/app/data/v1/data_pb2.py +238 -119
  83. viam/gen/app/data/v1/data_pb2.pyi +804 -34
  84. viam/gen/app/datapipelines/v1/data_pipelines_grpc.py +84 -0
  85. viam/gen/app/datapipelines/v1/data_pipelines_pb2.py +57 -0
  86. viam/gen/app/datapipelines/v1/data_pipelines_pb2.pyi +387 -0
  87. viam/gen/app/dataset/v1/dataset_grpc.py +10 -2
  88. viam/gen/app/dataset/v1/dataset_pb2.py +38 -31
  89. viam/gen/app/dataset/v1/dataset_pb2.pyi +36 -1
  90. viam/gen/app/datasync/v1/data_sync_grpc.py +1 -1
  91. viam/gen/app/datasync/v1/data_sync_pb2.py +61 -51
  92. viam/gen/app/datasync/v1/data_sync_pb2.pyi +52 -12
  93. viam/gen/app/mlinference/__init__.py +0 -0
  94. viam/gen/app/mlinference/v1/__init__.py +0 -0
  95. viam/gen/app/mlinference/v1/ml_inference_grpc.py +28 -0
  96. viam/gen/app/mlinference/v1/ml_inference_pb2.py +23 -0
  97. viam/gen/app/mlinference/v1/ml_inference_pb2.pyi +63 -0
  98. viam/gen/app/mltraining/v1/ml_training_grpc.py +18 -2
  99. viam/gen/app/mltraining/v1/ml_training_pb2.py +134 -101
  100. viam/gen/app/mltraining/v1/ml_training_pb2.pyi +193 -7
  101. viam/gen/app/packages/v1/packages_pb2.py +43 -40
  102. viam/gen/app/v1/app_grpc.py +290 -2
  103. viam/gen/app/v1/app_pb2.py +670 -453
  104. viam/gen/app/v1/app_pb2.pyi +3133 -947
  105. viam/gen/app/v1/billing_grpc.py +34 -2
  106. viam/gen/app/v1/billing_pb2.py +94 -35
  107. viam/gen/app/v1/billing_pb2.pyi +314 -61
  108. viam/gen/app/v1/end_user_pb2.py +50 -29
  109. viam/gen/app/v1/robot_pb2.py +120 -111
  110. viam/gen/app/v1/robot_pb2.pyi +137 -15
  111. viam/gen/common/v1/common_pb2.py +86 -66
  112. viam/gen/common/v1/common_pb2.pyi +184 -8
  113. viam/gen/component/arm/v1/arm_grpc.py +18 -2
  114. viam/gen/component/arm/v1/arm_pb2.py +68 -55
  115. viam/gen/component/arm/v1/arm_pb2.pyi +73 -3
  116. viam/gen/component/audioin/__init__.py +0 -0
  117. viam/gen/component/audioin/v1/__init__.py +0 -0
  118. viam/gen/component/audioin/v1/audioin_grpc.py +54 -0
  119. viam/gen/component/audioin/v1/audioin_pb2.py +34 -0
  120. viam/gen/component/audioin/v1/audioin_pb2.pyi +94 -0
  121. viam/gen/component/audioinput/v1/audioinput_pb2.py +35 -32
  122. viam/gen/component/audioout/__init__.py +0 -0
  123. viam/gen/component/audioout/v1/__init__.py +0 -0
  124. viam/gen/component/audioout/v1/audioout_grpc.py +54 -0
  125. viam/gen/component/audioout/v1/audioout_pb2.py +32 -0
  126. viam/gen/component/audioout/v1/audioout_pb2.pyi +47 -0
  127. viam/gen/component/base/v1/base_pb2.py +57 -54
  128. viam/gen/component/board/v1/board_pb2.py +93 -90
  129. viam/gen/component/button/__init__.py +0 -0
  130. viam/gen/component/button/v1/__init__.py +0 -0
  131. viam/gen/component/button/v1/button_grpc.py +38 -0
  132. viam/gen/component/button/v1/button_pb2.py +28 -0
  133. viam/gen/component/button/v1/button_pb2.pyi +39 -0
  134. viam/gen/component/camera/v1/camera_pb2.py +58 -55
  135. viam/gen/component/camera/v1/camera_pb2.pyi +31 -7
  136. viam/gen/component/encoder/v1/encoder_pb2.py +35 -32
  137. viam/gen/component/gantry/v1/gantry_grpc.py +9 -1
  138. viam/gen/component/gantry/v1/gantry_pb2.py +56 -51
  139. viam/gen/component/generic/v1/generic_pb2.py +15 -12
  140. viam/gen/component/gripper/v1/gripper_grpc.py +18 -2
  141. viam/gen/component/gripper/v1/gripper_pb2.py +48 -37
  142. viam/gen/component/gripper/v1/gripper_pb2.pyi +43 -1
  143. viam/gen/component/inputcontroller/v1/input_controller_pb2.py +45 -42
  144. viam/gen/component/motor/v1/motor_pb2.py +77 -74
  145. viam/gen/component/movementsensor/v1/movementsensor_pb2.py +69 -66
  146. viam/gen/component/posetracker/v1/pose_tracker_pb2.py +25 -22
  147. viam/gen/component/powersensor/v1/powersensor_pb2.py +33 -30
  148. viam/gen/component/sensor/v1/sensor_pb2.py +17 -14
  149. viam/gen/component/servo/v1/servo_pb2.py +41 -38
  150. viam/gen/component/switch/__init__.py +0 -0
  151. viam/gen/component/switch/v1/__init__.py +0 -0
  152. viam/gen/component/switch/v1/switch_grpc.py +54 -0
  153. viam/gen/component/switch/v1/switch_pb2.py +40 -0
  154. viam/gen/component/switch/v1/switch_pb2.pyi +116 -0
  155. viam/gen/component/testecho/v1/testecho_pb2.py +29 -26
  156. viam/gen/module/v1/module_pb2.py +36 -33
  157. viam/gen/module/v1/module_pb2.pyi +7 -2
  158. viam/gen/proto/rpc/examples/echo/v1/echo_pb2.py +26 -23
  159. viam/gen/proto/rpc/examples/echoresource/v1/echoresource_pb2.py +23 -20
  160. viam/gen/proto/rpc/v1/auth_pb2.py +27 -24
  161. viam/gen/proto/rpc/webrtc/v1/grpc_pb2.py +35 -32
  162. viam/gen/proto/rpc/webrtc/v1/signaling_pb2.py +62 -57
  163. viam/gen/proto/rpc/webrtc/v1/signaling_pb2.pyi +18 -4
  164. viam/gen/provisioning/v1/provisioning_grpc.py +10 -2
  165. viam/gen/provisioning/v1/provisioning_pb2.py +38 -31
  166. viam/gen/provisioning/v1/provisioning_pb2.pyi +20 -2
  167. viam/gen/robot/v1/robot_grpc.py +61 -29
  168. viam/gen/robot/v1/robot_pb2.py +186 -155
  169. viam/gen/robot/v1/robot_pb2.pyi +278 -59
  170. viam/gen/service/datamanager/v1/data_manager_grpc.py +11 -2
  171. viam/gen/service/datamanager/v1/data_manager_pb2.py +27 -17
  172. viam/gen/service/datamanager/v1/data_manager_pb2.pyi +47 -1
  173. viam/gen/service/discovery/__init__.py +0 -0
  174. viam/gen/service/discovery/v1/__init__.py +0 -0
  175. viam/gen/service/discovery/v1/discovery_grpc.py +39 -0
  176. viam/gen/service/discovery/v1/discovery_pb2.py +29 -0
  177. viam/gen/service/discovery/v1/discovery_pb2.pyi +51 -0
  178. viam/gen/service/generic/v1/generic_pb2.py +13 -10
  179. viam/gen/service/mlmodel/v1/mlmodel_pb2.py +75 -72
  180. viam/gen/service/motion/v1/motion_pb2.py +118 -85
  181. viam/gen/service/motion/v1/motion_pb2.pyi +130 -68
  182. viam/gen/service/navigation/v1/navigation_pb2.py +75 -72
  183. viam/gen/service/sensors/v1/sensors_pb2.py +59 -56
  184. viam/gen/service/shell/v1/shell_pb2.py +35 -32
  185. viam/gen/service/slam/v1/slam_pb2.py +43 -40
  186. viam/gen/service/slam/v1/slam_pb2.pyi +1 -1
  187. viam/gen/service/video/__init__.py +0 -0
  188. viam/gen/service/video/v1/__init__.py +0 -0
  189. viam/gen/service/video/v1/video_grpc.py +39 -0
  190. viam/gen/service/video/v1/video_pb2.py +29 -0
  191. viam/gen/service/video/v1/video_pb2.pyi +72 -0
  192. viam/gen/service/vision/v1/vision_pb2.py +60 -57
  193. viam/gen/service/vision/v1/vision_pb2.pyi +28 -3
  194. viam/gen/service/worldstatestore/__init__.py +0 -0
  195. viam/gen/service/worldstatestore/v1/__init__.py +0 -0
  196. viam/gen/service/worldstatestore/v1/world_state_store_grpc.py +55 -0
  197. viam/gen/service/worldstatestore/v1/world_state_store_pb2.py +39 -0
  198. viam/gen/service/worldstatestore/v1/world_state_store_pb2.pyi +171 -0
  199. viam/gen/stream/v1/stream_grpc.py +17 -1
  200. viam/gen/stream/v1/stream_pb2.py +34 -21
  201. viam/gen/stream/v1/stream_pb2.pyi +79 -1
  202. viam/gen/tagger/v1/tagger_pb2.py +9 -8
  203. viam/logging.py +77 -18
  204. viam/media/audio.py +28 -0
  205. viam/media/utils/pil/__init__.py +7 -3
  206. viam/media/video.py +80 -17
  207. viam/module/module.py +111 -38
  208. viam/module/resource_data_consumer.py +41 -0
  209. viam/module/service.py +9 -1
  210. viam/module/types.py +2 -4
  211. viam/proto/app/__init__.py +199 -0
  212. viam/proto/app/agent/__init__.py +5 -2
  213. viam/proto/app/billing.py +31 -4
  214. viam/proto/app/cloudslam/__init__.py +1 -0
  215. viam/proto/app/data/__init__.py +63 -0
  216. viam/proto/app/datapipelines/__init__.py +56 -0
  217. viam/proto/app/dataset/__init__.py +5 -0
  218. viam/proto/app/datasync/__init__.py +3 -0
  219. viam/proto/app/end_user.py +1 -0
  220. viam/proto/app/mlinference/__init__.py +15 -0
  221. viam/proto/app/mltraining/__init__.py +13 -0
  222. viam/proto/app/packages/__init__.py +1 -0
  223. viam/proto/app/robot.py +7 -0
  224. viam/proto/common/__init__.py +15 -0
  225. viam/proto/component/arm/__init__.py +7 -0
  226. viam/proto/component/audioin/__init__.py +16 -0
  227. viam/proto/component/audioinput/__init__.py +1 -0
  228. viam/proto/component/audioout/__init__.py +15 -0
  229. viam/proto/component/base/__init__.py +1 -0
  230. viam/proto/component/board/__init__.py +1 -0
  231. viam/proto/component/button/__init__.py +15 -0
  232. viam/proto/component/camera/__init__.py +1 -0
  233. viam/proto/component/encoder/__init__.py +1 -0
  234. viam/proto/component/gantry/__init__.py +1 -0
  235. viam/proto/component/generic/__init__.py +1 -0
  236. viam/proto/component/gripper/__init__.py +5 -0
  237. viam/proto/component/inputcontroller/__init__.py +1 -0
  238. viam/proto/component/motor/__init__.py +1 -0
  239. viam/proto/component/movementsensor/__init__.py +1 -0
  240. viam/proto/component/posetracker/__init__.py +1 -0
  241. viam/proto/component/powersensor/__init__.py +1 -0
  242. viam/proto/component/sensor/__init__.py +1 -0
  243. viam/proto/component/servo/__init__.py +1 -0
  244. viam/proto/component/switch/__init__.py +26 -0
  245. viam/proto/component/testecho/__init__.py +1 -0
  246. viam/proto/module/__init__.py +1 -0
  247. viam/proto/provisioning/__init__.py +5 -0
  248. viam/proto/robot/__init__.py +29 -8
  249. viam/proto/rpc/auth.py +1 -0
  250. viam/proto/rpc/examples/echo/__init__.py +1 -0
  251. viam/proto/rpc/examples/echoresource/__init__.py +1 -0
  252. viam/proto/rpc/webrtc/grpc.py +1 -0
  253. viam/proto/rpc/webrtc/signaling.py +3 -0
  254. viam/proto/service/datamanager/__init__.py +9 -1
  255. viam/proto/service/discovery/__init__.py +15 -0
  256. viam/proto/service/generic/__init__.py +1 -0
  257. viam/proto/service/mlmodel/__init__.py +1 -0
  258. viam/proto/service/motion/__init__.py +3 -0
  259. viam/proto/service/navigation/__init__.py +1 -0
  260. viam/proto/service/sensors/__init__.py +1 -0
  261. viam/proto/service/shell/__init__.py +1 -0
  262. viam/proto/service/slam/__init__.py +1 -0
  263. viam/proto/service/video/__init__.py +15 -0
  264. viam/proto/service/vision/__init__.py +1 -0
  265. viam/proto/service/worldstatestore/__init__.py +32 -0
  266. viam/proto/stream/__init__.py +11 -0
  267. viam/py.typed +0 -0
  268. viam/resource/base.py +12 -8
  269. viam/resource/easy_resource.py +24 -13
  270. viam/resource/manager.py +6 -5
  271. viam/resource/registry.py +39 -51
  272. viam/resource/rpc_client_base.py +33 -1
  273. viam/resource/types.py +13 -14
  274. viam/robot/client.py +190 -122
  275. viam/robot/service.py +2 -50
  276. viam/rpc/dial.py +54 -4
  277. viam/rpc/libviam_rust_utils.so +0 -0
  278. viam/rpc/server.py +25 -11
  279. viam/rpc/types.py +2 -4
  280. viam/services/discovery/__init__.py +12 -0
  281. viam/services/discovery/client.py +55 -0
  282. viam/services/discovery/discovery.py +52 -0
  283. viam/services/discovery/service.py +43 -0
  284. viam/services/generic/__init__.py +1 -1
  285. viam/services/generic/client.py +8 -5
  286. viam/services/generic/generic.py +2 -2
  287. viam/services/mlmodel/__init__.py +1 -1
  288. viam/services/mlmodel/client.py +17 -7
  289. viam/services/mlmodel/mlmodel.py +23 -12
  290. viam/services/mlmodel/service.py +5 -2
  291. viam/services/mlmodel/utils.py +11 -1
  292. viam/services/motion/__init__.py +2 -2
  293. viam/services/motion/client.py +32 -32
  294. viam/services/motion/motion.py +66 -62
  295. viam/services/navigation/__init__.py +1 -1
  296. viam/services/navigation/client.py +30 -20
  297. viam/services/navigation/navigation.py +23 -23
  298. viam/services/service_base.py +13 -9
  299. viam/services/service_client_base.py +3 -3
  300. viam/services/slam/__init__.py +1 -1
  301. viam/services/slam/client.py +15 -10
  302. viam/services/slam/slam.py +11 -11
  303. viam/services/vision/__init__.py +1 -1
  304. viam/services/vision/client.py +31 -24
  305. viam/services/vision/service.py +8 -8
  306. viam/services/vision/vision.py +36 -53
  307. viam/services/worldstatestore/__init__.py +18 -0
  308. viam/services/worldstatestore/client.py +94 -0
  309. viam/services/worldstatestore/service.py +55 -0
  310. viam/services/worldstatestore/worldstatestore.py +90 -0
  311. viam/sessions_client.py +115 -46
  312. viam/streams.py +3 -6
  313. viam/utils.py +44 -14
  314. viam/version_metadata.py +4 -0
  315. {viam_sdk-0.25.2.dist-info → viam_sdk-0.62.0.dist-info}/METADATA +27 -28
  316. viam_sdk-0.62.0.dist-info/RECORD +514 -0
  317. {viam_sdk-0.25.2.dist-info → viam_sdk-0.62.0.dist-info}/WHEEL +1 -1
  318. viam/gen/proto/rpc/examples/fileupload/v1/fileupload_grpc.py +0 -27
  319. viam/gen/proto/rpc/examples/fileupload/v1/fileupload_pb2.py +0 -18
  320. viam/gen/proto/rpc/examples/fileupload/v1/fileupload_pb2.pyi +0 -45
  321. viam/proto/rpc/examples/fileupload/__init__.py +0 -18
  322. viam/services/sensors/__init__.py +0 -5
  323. viam/services/sensors/client.py +0 -65
  324. viam_sdk-0.25.2.dist-info/LICENSE +0 -202
  325. viam_sdk-0.25.2.dist-info/RECORD +0 -442
  326. /viam/gen/{proto/rpc/examples/fileupload → app/datapipelines}/__init__.py +0 -0
  327. /viam/gen/{proto/rpc/examples/fileupload → app/datapipelines}/v1/__init__.py +0 -0
  328. /LICENSE → /viam_sdk-0.62.0.dist-info/licenses/LICENSE +0 -0
@@ -0,0 +1,90 @@
1
+ import abc
2
+ from typing import Any, AsyncGenerator, Final, List, Mapping, Optional
3
+
4
+ from viam.proto.common import Transform
5
+ from viam.proto.service.worldstatestore import (
6
+ StreamTransformChangesResponse,
7
+ )
8
+ from viam.resource.types import API, RESOURCE_NAMESPACE_RDK, RESOURCE_TYPE_SERVICE
9
+
10
+ from ..service_base import ServiceBase
11
+
12
+
13
+ class WorldStateStore(ServiceBase):
14
+ """WorldStateStore is a Viam service that manages world state transforms.
15
+
16
+ The WorldStateStore service provides functionality to store, retrieve, and stream
17
+ changes to world state transforms, which represent the pose of objects in different
18
+ reference frames. This functionality can be used to create custom visualizations of the world state.
19
+
20
+ For more information, see `WorldStateStore service <https://docs.viam.com/dev/reference/apis/services/world-state-store/>`_.
21
+ """
22
+
23
+ API: Final = API( # pyright: ignore [reportIncompatibleVariableOverride]
24
+ RESOURCE_NAMESPACE_RDK, RESOURCE_TYPE_SERVICE, "world_state_store"
25
+ )
26
+
27
+ @abc.abstractmethod
28
+ async def list_uuids(
29
+ self,
30
+ *,
31
+ extra: Optional[Mapping[str, Any]] = None,
32
+ timeout: Optional[float] = None,
33
+ ) -> List[bytes]:
34
+ """List all world state transform UUIDs.
35
+
36
+ ::
37
+
38
+ worldstatestore = WorldStateStoreClient.from_robot(robot=machine, name="builtin")
39
+
40
+ uuids = await worldstatestore.list_uuids()
41
+
42
+ Returns:
43
+ List[bytes]: A list of transform UUIDs
44
+ """
45
+ ...
46
+
47
+ @abc.abstractmethod
48
+ async def get_transform(
49
+ self,
50
+ uuid: bytes,
51
+ *,
52
+ extra: Optional[Mapping[str, Any]] = None,
53
+ timeout: Optional[float] = None,
54
+ ) -> Transform:
55
+ """Get a world state transform by UUID.
56
+
57
+ ::
58
+
59
+ worldstatestore = WorldStateStoreClient.from_robot(robot=machine, name="builtin")
60
+
61
+ transform = await worldstatestore.get_transform(uuid=b"some-uuid")
62
+
63
+ Args:
64
+ uuid (bytes): The UUID of the transform to retrieve
65
+
66
+ Returns:
67
+ Transform: The requested transform
68
+ """
69
+ ...
70
+
71
+ @abc.abstractmethod
72
+ async def stream_transform_changes(
73
+ self,
74
+ *,
75
+ extra: Optional[Mapping[str, Any]] = None,
76
+ timeout: Optional[float] = None,
77
+ ) -> AsyncGenerator[StreamTransformChangesResponse, None]:
78
+ """Stream changes to world state transforms.
79
+
80
+ ::
81
+
82
+ worldstatestore = WorldStateStoreClient.from_robot(robot=machine, name="builtin")
83
+
84
+ async for change in worldstatestore.stream_transform_changes():
85
+ print(f"Transform {change.transform.uuid} {change.change_type}")
86
+
87
+ Returns:
88
+ AsyncGenerator[StreamTransformChangesResponse, None]: A stream of transform changes
89
+ """
90
+ ...
viam/sessions_client.py CHANGED
@@ -1,9 +1,14 @@
1
1
  import asyncio
2
+ import importlib
3
+ import pkgutil
4
+ import sys
5
+ from concurrent.futures import ThreadPoolExecutor
6
+ from contextlib import asynccontextmanager
2
7
  from copy import deepcopy
3
8
  from datetime import timedelta
4
9
  from enum import IntEnum
5
10
  from threading import Lock, Thread
6
- from typing import Optional
11
+ from typing import MutableMapping, Optional
7
12
 
8
13
  from grpclib import Status
9
14
  from grpclib.client import Channel
@@ -12,26 +17,13 @@ from grpclib.exceptions import GRPCError, StreamTerminatedError
12
17
  from grpclib.metadata import _MetadataLike
13
18
 
14
19
  from viam import logging
20
+ from viam.gen.common.v1.common_pb2 import safety_heartbeat_monitored
15
21
  from viam.proto.robot import RobotServiceStub, SendSessionHeartbeatRequest, StartSessionRequest, StartSessionResponse
16
22
  from viam.rpc.dial import DialOptions, dial
17
23
 
18
24
  LOGGER = logging.getLogger(__name__)
19
25
  SESSION_METADATA_KEY = "viam-sid"
20
26
 
21
- EXEMPT_METADATA_METHODS = frozenset(
22
- [
23
- "/grpc.reflection.v1alpha.ServerReflection/ServerReflectionInfo",
24
- "/proto.rpc.webrtc.v1.SignalingService/Call",
25
- "/proto.rpc.webrtc.v1.SignalingService/CallUpdate",
26
- "/proto.rpc.webrtc.v1.SignalingService/OptionalWebRTCConfig",
27
- "/proto.rpc.v1.AuthService/Authenticate",
28
- "/viam.robot.v1.RobotService/ResourceNames",
29
- "/viam.robot.v1.RobotService/ResourceRPCSubtypes",
30
- "/viam.robot.v1.RobotService/StartSession",
31
- "/viam.robot.v1.RobotService/SendSessionHeartbeat",
32
- ]
33
- )
34
-
35
27
 
36
28
  class _SupportedState(IntEnum):
37
29
  UNKNOWN = 0
@@ -47,7 +39,8 @@ class SessionsClient:
47
39
 
48
40
  channel: Channel
49
41
  client: RobotServiceStub
50
- _address: str
42
+ _address: str # direct dial address, when using webRTC this is the local socket rather than a robot address
43
+ _robot_address: Optional[str] # the actual machine address on app.viam.com. important for creating a sessions client on Windows
51
44
  _dial_options: DialOptions
52
45
  _disabled: bool
53
46
  _lock: Lock
@@ -55,19 +48,33 @@ class SessionsClient:
55
48
  _heartbeat_interval: Optional[timedelta]
56
49
  _supported: _SupportedState
57
50
  _thread: Optional[Thread]
58
-
59
- def __init__(self, channel: Channel, direct_dial_address: str, dial_options: Optional[DialOptions], *, disabled: bool = False):
51
+ _pool: ThreadPoolExecutor
52
+
53
+ _HEARTBEAT_MONITORED_METHODS: MutableMapping[str, bool] = {}
54
+
55
+ def __init__(
56
+ self,
57
+ channel: Channel,
58
+ direct_dial_address: str,
59
+ dial_options: Optional[DialOptions],
60
+ *,
61
+ disabled: bool = False,
62
+ robot_addr: Optional[str] = None,
63
+ ):
60
64
  self.channel = channel
61
65
  self.client = RobotServiceStub(channel)
62
66
  self._address = direct_dial_address
67
+ self._robot_address = robot_addr
63
68
  self._disabled = disabled
64
69
  self._dial_options = deepcopy(dial_options) if dial_options is not None else DialOptions()
65
- self._dial_options.disable_webrtc = True
70
+ if sys.platform != "win32" and sys.platform != "cygwin":
71
+ self._dial_options.disable_webrtc = True
66
72
  self._lock = Lock()
67
73
  self._current_id = ""
68
74
  self._heartbeat_interval = None
69
75
  self._supported = _SupportedState.UNKNOWN
70
76
  self._thread = None
77
+ self._pool = ThreadPoolExecutor()
71
78
 
72
79
  listen(self.channel, SendRequest, self._send_request)
73
80
  listen(self.channel, RecvTrailingMetadata, self._recv_trailers)
@@ -92,7 +99,7 @@ class SessionsClient:
92
99
  if self._disabled:
93
100
  return
94
101
 
95
- if event.method_name in EXEMPT_METADATA_METHODS:
102
+ if not self._is_safety_heartbeat_monitored(event.method_name):
96
103
  return
97
104
 
98
105
  event.metadata.update(await self.metadata)
@@ -102,39 +109,45 @@ class SessionsClient:
102
109
  LOGGER.debug("Session expired")
103
110
  self.reset()
104
111
 
112
+ @asynccontextmanager
113
+ async def _acquire_lock_async(self):
114
+ loop = asyncio.get_event_loop()
115
+ await loop.run_in_executor(self._pool, self._lock.acquire)
116
+ try:
117
+ yield
118
+ finally:
119
+ self._lock.release()
120
+
105
121
  @property
106
122
  async def metadata(self) -> _MetadataLike:
107
- with self._lock:
123
+ async with self._acquire_lock_async():
108
124
  if self._disabled or self._supported != _SupportedState.UNKNOWN:
109
125
  return self._metadata
110
126
 
111
- request = StartSessionRequest(resume=self._current_id)
112
- try:
113
- response: StartSessionResponse = await self.client.StartSession(request)
114
- except GRPCError as error:
115
- if error.status == Status.UNIMPLEMENTED:
116
- with self._lock:
127
+ request = StartSessionRequest(resume=self._current_id)
128
+ try:
129
+ response: StartSessionResponse = await self.client.StartSession(request)
130
+ except GRPCError as error:
131
+ if error.status == Status.UNIMPLEMENTED:
117
132
  self._reset()
118
133
  self._supported = _SupportedState.FALSE
119
134
  return self._metadata
120
- else:
121
- raise
135
+ else:
136
+ raise
122
137
 
123
- if response is None:
124
- raise GRPCError(status=Status.INTERNAL, message="Expected response to start session")
138
+ if response is None:
139
+ raise GRPCError(status=Status.INTERNAL, message="Expected response to start session")
125
140
 
126
- if response.heartbeat_window is None:
127
- raise GRPCError(status=Status.INTERNAL, message="Expected heartbeat window in response to start session")
141
+ if response.heartbeat_window is None:
142
+ raise GRPCError(status=Status.INTERNAL, message="Expected heartbeat window in response to start session")
128
143
 
129
- with self._lock:
130
144
  self._supported = _SupportedState.TRUE
131
145
  self._heartbeat_interval = response.heartbeat_window.ToTimedelta()
132
146
  self._current_id = response.id
133
147
 
134
- # tick once to ensure heartbeats are supported
135
- await self._heartbeat_tick(self.client)
148
+ # tick once to ensure heartbeats are supported
149
+ await self._heartbeat_tick(self.client)
136
150
 
137
- with self._lock:
138
151
  if self._thread is not None:
139
152
  self._reset()
140
153
  if self._supported == _SupportedState.TRUE:
@@ -153,28 +166,38 @@ class SessionsClient:
153
166
  return self._metadata
154
167
 
155
168
  async def _heartbeat_tick(self, client: RobotServiceStub):
156
- with self._lock:
157
- if not self._current_id:
158
- LOGGER.debug("Failed to send heartbeat, session client reset")
159
- return
160
- request = SendSessionHeartbeatRequest(id=self._current_id)
169
+ if not self._current_id:
170
+ LOGGER.debug("Failed to send heartbeat, session client reset")
171
+ return
172
+ request = SendSessionHeartbeatRequest(id=self._current_id)
161
173
 
162
174
  try:
163
175
  await client.SendSessionHeartbeat(request)
164
176
  except (GRPCError, StreamTerminatedError):
165
177
  LOGGER.debug("Heartbeat terminated", exc_info=True)
166
- self.reset()
178
+ self._reset()
167
179
  else:
168
180
  LOGGER.debug("Sent heartbeat successfully")
169
181
 
182
+ def _get_local_addr(self) -> str:
183
+ if sys.platform != "win32" and sys.platform != "cygwin":
184
+ # if we're not on windows, we want the direct dial address
185
+ return self._address
186
+
187
+ # return `robot_address` if it exists, otherwise fallback
188
+ # when using TCP (i.e., on Windows), we need to create a connection to the actual
189
+ # robot address for a sessions client to maintain connectivity successfully
190
+ return self._robot_address if self._robot_address is not None else self._address
191
+
170
192
  async def _heartbeat_process(self, wait: float):
171
- channel = await dial(address=self._address, options=self._dial_options)
193
+ addr = self._get_local_addr()
194
+ channel = await dial(address=addr, options=self._dial_options)
172
195
  client = RobotServiceStub(channel.channel)
173
196
  while True:
174
- with self._lock:
197
+ async with self._acquire_lock_async():
175
198
  if self._supported != _SupportedState.TRUE:
176
199
  return
177
- await self._heartbeat_tick(client)
200
+ await self._heartbeat_tick(client)
178
201
  await asyncio.sleep(wait)
179
202
 
180
203
  @property
@@ -183,3 +206,49 @@ class SessionsClient:
183
206
  return {SESSION_METADATA_KEY: self._current_id}
184
207
 
185
208
  return {}
209
+
210
+ def _is_safety_heartbeat_monitored(self, method: str) -> bool:
211
+ if method in self._HEARTBEAT_MONITORED_METHODS:
212
+ return self._HEARTBEAT_MONITORED_METHODS[method]
213
+
214
+ parts = method.split("/")
215
+ if len(parts) != 3:
216
+ self._HEARTBEAT_MONITORED_METHODS[method] = False
217
+ return False
218
+ service_path = parts[1]
219
+ method_name = parts[2]
220
+
221
+ parts = service_path.split(".")
222
+ if len(parts) < 5:
223
+ self._HEARTBEAT_MONITORED_METHODS[method] = False
224
+ return False
225
+ if parts[0] != "viam":
226
+ self._HEARTBEAT_MONITORED_METHODS[method] = False
227
+ return False
228
+ resource_type = parts[1]
229
+ resource_subtype = parts[2]
230
+ version = parts[3]
231
+ service_name = parts[4]
232
+ try:
233
+ module = importlib.import_module(f"viam.gen.{resource_type}.{resource_subtype}.{version}")
234
+ submods = pkgutil.iter_modules(module.__path__)
235
+ for mod in submods:
236
+ if "_pb2" in mod.name:
237
+ submod = getattr(module, mod.name)
238
+ DESCRIPTOR = getattr(submod, "DESCRIPTOR")
239
+ for service in DESCRIPTOR.services_by_name.values():
240
+ if service.name == service_name:
241
+ for method_actual in service.methods:
242
+ if method_actual.name == method_name:
243
+ options = method_actual.GetOptions()
244
+ if options.HasExtension(safety_heartbeat_monitored):
245
+ is_monitored = options.Extensions[safety_heartbeat_monitored]
246
+ self._HEARTBEAT_MONITORED_METHODS[method] = is_monitored
247
+ return is_monitored
248
+ self._HEARTBEAT_MONITORED_METHODS[method] = False
249
+ return False
250
+ self._HEARTBEAT_MONITORED_METHODS[method] = False
251
+ return False
252
+ except Exception:
253
+ self._HEARTBEAT_MONITORED_METHODS[method] = False
254
+ return False
viam/streams.py CHANGED
@@ -11,8 +11,7 @@ StreamType = TypeVar("StreamType", covariant=True)
11
11
 
12
12
 
13
13
  class Stream(Protocol[StreamType]):
14
- async def next(self) -> StreamType:
15
- ...
14
+ async def next(self) -> StreamType: ...
16
15
 
17
16
  def __aiter__(self) -> AsyncIterator:
18
17
  return self
@@ -22,13 +21,11 @@ class Stream(Protocol[StreamType]):
22
21
 
23
22
 
24
23
  class StreamReader(Protocol[StreamType]):
25
- async def read(self) -> StreamType:
26
- ...
24
+ async def read(self) -> StreamType: ...
27
25
 
28
26
 
29
27
  class StreamSource(Protocol[StreamType]):
30
- async def stream(self) -> Stream[StreamType]:
31
- ...
28
+ async def stream(self) -> Stream[StreamType]: ...
32
29
 
33
30
 
34
31
  class StreamWithIterator(Stream[StreamType]):
viam/utils.py CHANGED
@@ -4,7 +4,7 @@ import functools
4
4
  import sys
5
5
  import threading
6
6
  from datetime import datetime
7
- from typing import Any, Dict, List, Mapping, Optional, SupportsBytes, SupportsFloat, Type, TypeVar, Union
7
+ from typing import Any, Callable, Dict, List, Mapping, Optional, SupportsBytes, SupportsFloat, Type, TypeVar, Union
8
8
 
9
9
  from google.protobuf.json_format import MessageToDict, ParseDict
10
10
  from google.protobuf.message import Message
@@ -15,7 +15,8 @@ from viam.proto.app.data import CaptureInterval, Filter, TagsFilter
15
15
  from viam.proto.common import Geometry, GeoPoint, GetGeometriesRequest, GetGeometriesResponse, Orientation, ResourceName, Vector3
16
16
  from viam.resource.base import ResourceBase
17
17
  from viam.resource.registry import Registry
18
- from viam.resource.types import Subtype, SupportsGetGeometries
18
+ from viam.resource.rpc_client_base import ResourceRPCClientBase
19
+ from viam.resource.types import API, SupportsGetGeometries
19
20
 
20
21
  if sys.version_info >= (3, 9):
21
22
  from collections.abc import Callable
@@ -103,14 +104,10 @@ def resource_names_for_resource(resource: ResourceBase) -> List[ResourceName]:
103
104
  rns: List[ResourceName] = []
104
105
 
105
106
  for klass in resource.__class__.mro():
106
- for registration in Registry.REGISTERED_SUBTYPES().values():
107
+ for registration in Registry.REGISTERED_APIS().values():
107
108
  if klass is registration.resource_type:
108
- subtype: Subtype = registration.resource_type.SUBTYPE
109
- rns.append(
110
- ResourceName(
111
- namespace=subtype.namespace, type=subtype.resource_type, subtype=subtype.resource_subtype, name=resource.name
112
- )
113
- )
109
+ api: API = registration.resource_type.API
110
+ rns.append(ResourceName(namespace=api.namespace, type=api.resource_type, subtype=api.resource_subtype, name=resource.name))
114
111
  return rns
115
112
 
116
113
 
@@ -134,7 +131,7 @@ def struct_to_message(struct: Struct, message_type: Type[_T]) -> _T:
134
131
  return ParseDict(dct, message_type())
135
132
 
136
133
 
137
- def dict_to_struct(obj: Mapping[str, ValueTypes]) -> Struct:
134
+ def dict_to_struct(obj: Optional[Mapping[str, ValueTypes]]) -> Struct:
138
135
  def _convert(v: ValueTypes) -> Any:
139
136
  if isinstance(v, bool):
140
137
  return v
@@ -148,6 +145,8 @@ def dict_to_struct(obj: Mapping[str, ValueTypes]) -> Struct:
148
145
  return {k: _convert(vv) for (k, vv) in v.items()}
149
146
  return v
150
147
 
148
+ if obj is None:
149
+ obj = {}
151
150
  struct = Struct()
152
151
  struct.update({k: _convert(v) for (k, v) in obj.items()})
153
152
  return struct
@@ -166,12 +165,15 @@ def datetime_to_timestamp(dt: Optional[datetime]) -> Optional[Timestamp]:
166
165
 
167
166
 
168
167
  async def get_geometries(
169
- client: SupportsGetGeometries, name: str, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None
168
+ client: SupportsGetGeometries,
169
+ name: str,
170
+ extra: Optional[Dict[str, Any]] = None,
171
+ timeout: Optional[float] = None,
172
+ metadata: ResourceRPCClientBase.Metadata = ResourceRPCClientBase.Metadata(),
170
173
  ) -> List[Geometry]:
171
- if extra is None:
172
- extra = {}
174
+ md = metadata.proto
173
175
  request = GetGeometriesRequest(name=name, extra=dict_to_struct(extra))
174
- response: GetGeometriesResponse = await client.GetGeometries(request, timeout=timeout)
176
+ response: GetGeometriesResponse = await client.GetGeometries(request, timeout=timeout, metadata=md)
175
177
  return [geometry for geometry in response.geometries]
176
178
 
177
179
 
@@ -333,3 +335,31 @@ def create_filter(
333
335
  bbox_labels=bbox_labels,
334
336
  dataset_id=dataset_id if dataset_id else "",
335
337
  )
338
+
339
+
340
+ def _alias_param(param_name: str, param_alias: str) -> Callable:
341
+ """
342
+ Decorator for aliasing a param in a function. Intended for providing backwards compatibility on params with name changes.
343
+
344
+ Args:
345
+ param_name: name of param in function to alias
346
+ param_alias: alias that can be used for this param
347
+ Returns:
348
+ The input function, plus param alias.
349
+ """
350
+
351
+ def decorator(func: Callable):
352
+ @functools.wraps(func)
353
+ def wrapper(*args, **kwargs):
354
+ alias_param_value = kwargs.get(param_alias)
355
+ if alias_param_value:
356
+ # Only use alias value if param is not given.
357
+ if not kwargs.get(param_name):
358
+ kwargs[param_name] = alias_param_value
359
+ del kwargs[param_alias]
360
+ result = func(*args, **kwargs)
361
+ return result
362
+
363
+ return wrapper
364
+
365
+ return decorator
@@ -0,0 +1,4 @@
1
+ __version__ = "0.62.0"
2
+
3
+ API_VERSION = "v0.1.495"
4
+ SDK_VERSION = __version__
@@ -1,26 +1,21 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: viam-sdk
3
- Version: 0.25.2
3
+ Version: 0.62.0
4
4
  Summary: Viam Robotics Python SDK
5
- Home-page: https://www.viam.com
6
- License: Apache-2.0
7
- Author: Naveed
8
- Author-email: naveed@viam.com
9
- Requires-Python: >=3.8.1,<3.13
10
- Classifier: License :: OSI Approved :: Apache Software License
11
- Classifier: Programming Language :: Python :: 3
12
- Classifier: Programming Language :: Python :: 3.9
13
- Classifier: Programming Language :: Python :: 3.10
14
- Classifier: Programming Language :: Python :: 3.11
15
- Classifier: Programming Language :: Python :: 3.12
16
- Provides-Extra: mlmodel
17
- Requires-Dist: googleapis-common-protos (>=1.60.0,<2)
18
- Requires-Dist: grpclib (>=0.4.7,<1)
19
- Requires-Dist: numpy (>=1.21,<2) ; extra == "mlmodel"
20
- Requires-Dist: protobuf (>=4.24.3)
21
- Requires-Dist: typing-extensions (>=4.8.0,<5)
5
+ Project-URL: Homepage, https://www.viam.com
22
6
  Project-URL: Documentation, https://python.viam.dev
23
7
  Project-URL: Repository, https://github.com/viamrobotics/viam-python-sdk
8
+ Author-email: Naveed Jooma <naveed@viam.com>
9
+ License-Expression: Apache-2.0
10
+ License-File: LICENSE
11
+ Requires-Python: >=3.8.1
12
+ Requires-Dist: googleapis-common-protos>=1.65.0
13
+ Requires-Dist: grpclib>=0.4.7
14
+ Requires-Dist: protobuf==5.29.5
15
+ Requires-Dist: pymongo>=4.10.1
16
+ Requires-Dist: typing-extensions>=4.12.2
17
+ Provides-Extra: mlmodel
18
+ Requires-Dist: numpy; extra == 'mlmodel'
24
19
  Description-Content-Type: text/markdown
25
20
 
26
21
  # Viam Python SDK
@@ -43,14 +38,18 @@ Currently, we have pre-built binaries for macOS (both Intel `x86_64` and Apple S
43
38
 
44
39
  `pip install viam-sdk`
45
40
 
41
+ If you want to install on Windows, you can install from github directly with `pip`:
42
+
43
+ `pip install git+https://github.com/viamrobotics/viam-python-sdk.git`
44
+
45
+ Note that only direct gRPC connections are supported on Windows; you will need to [disable webrtc](https://python.viam.dev/autoapi/viam/rpc/dial/index.html#viam.rpc.dial.DialOptions.disable_webrtc) or else connection will fail. Full support (including webRTC) _does_ exist on WSL.
46
+
46
47
  If you intend to use the [`MLModel` service](https://python.viam.dev/autoapi/viam/services/mlmodel/mlmodel/index.html#viam.services.mlmodel.mlmodel.MLModel), use the following command instead, which installs additional required dependencies:
47
48
 
48
49
  `pip install 'viam-sdk[mlmodel]'`
49
50
 
50
51
  You can also run this command on an existing Python SDK install to add support for the ML model service.
51
- See the [ML (machine learning) model service](https://docs.viam.com/ml/) documentation for more information.
52
-
53
- Windows is not supported. If you are using Windows, install `viam-sdk` in WSL. For other unsupported systems, read further on how to install from source.
52
+ See the [ML (machine learning) model service](https://docs.viam.com/data-ai/ai/deploy/) documentation for more information.
54
53
 
55
54
  ### Upgrading
56
55
 
@@ -70,7 +69,7 @@ The Viam Python SDK uses native libraries to support communication over WebRTC,
70
69
  - If you provided the `--release` flag, the enclosing directory will be `release`: `rust-utils/target/release/libviam_rust_utils.*`
71
70
  - The extension of the executable will depend on your operating system. For example, on macOS it will be `libviam_rust_utils.dylib`, whereas on Linux it will be `libviam_rust_utils.so`
72
71
  1. Copy the compiled library to the directory `viam-python-sdk/src/viam/rpc/`
73
- 1. From the `viam-python-sdk` directory, run `poetry build` to create an installable package
72
+ 1. From the `viam-python-sdk` directory, run `uv build --wheel` to create an installable package
74
73
  1. Find the newly created installable package located in `viam-python-sdk/dist/` and pip install it directly, for example: `pip install viam-python-sdk/dist/viam_sdk-0.1.0-py3-none-any.whl`
75
74
 
76
75
  If you have a macOS or Linux based operating system and do not want to build rust-utils manually, you can also look for the executable in the [releases](https://github.com/viamrobotics/rust-utils/releases/latest) page of the rust-utils library.
@@ -78,7 +77,7 @@ If you have a macOS or Linux based operating system and do not want to build rus
78
77
  If you do **NOT** need communication over WebRTC (and thus, do not need the native library), the steps are:
79
78
 
80
79
  1. Download/clone this repository
81
- 1. Run `poetry build` from the `viam-python-sdk` directory
80
+ 1. Run `uv build --wheel` from the `viam-python-sdk` directory
82
81
  1. Find the newly created installable package located in `viam-python-sdk/dist/` and pip install it directly, for example: `pip install viam-python-sdk/dist/viam_sdk-0.1.0-py3-none-any.whl`
83
82
  1. Ensure that every connection has the option `disable_webrtc` set to `True`: `viam.rpc.dial.DialOptions(disable_webrtc=True)`
84
83
  - For more information about connecting to a robot, see the [documentation](https://python.viam.dev) and [example usage](https://python.viam.dev/examples/example.html)
@@ -92,7 +91,8 @@ To create a client application, to navigate to [app.viam.com](https://app.viam.c
92
91
  1. Create a location (for example `home`)
93
92
  2. Create a robot (for example `arduino`)
94
93
  3. Follow the steps on the setup tab:
95
- 1. Setup Viam App Config on Single Board Computer (SBC)
94
+
95
+ 1. Setup machine cloud credentials on Single Board Computer (SBC)
96
96
  2. Download and Install Viam Server
97
97
  3. Wait until the robot shows as connected. If this doesn't happen try restarting the viam-server:
98
98
 
@@ -146,13 +146,12 @@ The SDK provides a number of abstract base components and services (collectively
146
146
  1. Define all requirements of the resource in `{RESOURCE_NAME}.py`
147
147
  1. Implement the gRPC service for the new resource in `service.py`
148
148
  1. Create a gRPC client for the new resource in `client.py`
149
- 1. Register the subtype and define package exports in `__init__.py`
149
+ 1. Register the API and define package exports in `__init__.py`
150
150
  1. Write tests for the new resource and add the resource to `tests.mocks.{components|services}`
151
151
  1. If the resource is a component, add the component to `examples.server.v1.components` and its corresponding concrete type in `examples.server.v1.server`
152
152
 
153
153
  ## License
154
154
 
155
- Copyright 2021-2023 Viam Inc.
155
+ Copyright 2021-2024 Viam Inc.
156
156
 
157
157
  Apache 2.0 - See [LICENSE](https://github.com/viamrobotics/viam-python-sdk/blob/main/LICENSE) file
158
-