Skip to content

Commit fc83b5a

Browse files
Merge branch 'viamrobotics:main' into rsdk-4788
2 parents 9750371 + ebc64b9 commit fc83b5a

30 files changed

+552
-293
lines changed

README.md

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,55 @@
11
# Viam Python SDK
2+
23
![PyPI](https://img.shields.io/pypi/v/viam-sdk)
34
![PyPI - Python Version](https://img.shields.io/pypi/pyversions/viam-sdk)
45
[![documentation](https://img.shields.io/static/v1?label=docs&message=python.viam.dev&color=lightgray)](https://python.viam.dev)
56
![build status](https://github.com/viamrobotics/python-sdk/actions/workflows/test.yml/badge.svg)
67
[![license](https://img.shields.io/badge/license-Apache_2.0-blue)](https://github.com/viamrobotics/viam-python-sdk/blob/main/LICENSE)
78

9+
The Viam Python SDK allows you to build robots, access existing Viam robots, and manage your fleet of Viam robots.
10+
11+
If you would like a blueprint on setting up a Python environment with Viam from scratch, you can follow our [Setup](https://python.viam.dev/setup.html) guide.
12+
13+
If you would like to develop and contribute to Viam's Python SDK, take a look at the [Development](#development) portion of the README.
14+
815
## Installation
16+
917
Currently, we have pre-built binaries for macOS (both Intel and Apple Silicon), along with Linux (x86, aarch64, armv6l) that you can install via pip
1018

1119
`pip install viam-sdk`
1220

1321
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.
1422

1523
### Upgrading
24+
1625
To upgrade, simply run the `pip install` command with the `-U` option:
1726
`pip install -U viam-sdk`
1827

1928
### Installing from Source
29+
2030
The Viam Python SDK uses native libraries to support communication over WebRTC, which will allow you to connect to robots that are not on the same network. In order to facilitate that communication, there is a [rust-utils repo](https://github.com/viamrobotics/rust-utils) that contains the necessary protocols. Therefore, to build from source, you will need both the Rust utils and the Rust compiler.
2131

2232
1. Download/clone this [repository](https://github.com/viamrobotics/viam-python-sdk)
2333
1. Download/clone the [rust-utils](https://github.com/viamrobotics/rust-utils)
2434
1. [Install Rust](https://www.rust-lang.org/tools/install) if not already available
2535
1. From the `rust-utils` directory, run `cargo build`
26-
* You can optionally provide the `--release` flag: `cargo build --release`
36+
- You can optionally provide the `--release` flag: `cargo build --release`
2737
1. Find the compiled library in `rust-utils/target/debug/libviam_rust_utils.*`
28-
* If you provided the `--release` flag, the enclosing directory will be `release`: `rust-utils/target/release/libviam_rust_utils.*`
29-
* 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`
38+
- If you provided the `--release` flag, the enclosing directory will be `release`: `rust-utils/target/release/libviam_rust_utils.*`
39+
- 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`
3040
1. Copy the compiled library to the directory `viam-python-sdk/src/viam/rpc/`
3141
1. From the `viam-python-sdk` directory, run `poetry build` to create an installable package
3242
1. Find the newly created installable package located in `viam-python-sdk/dist/` and pip install it directly, e.g.: `pip install viam-python-sdk/dist/viam_sdk-0.1.0-py3-none-any.whl`
3343

3444
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.
3545

36-
3746
If you do **NOT** need communication over WebRTC (and thus, do not need the native library), the steps are:
3847

3948
1. Download/clone this repository
4049
1. Run `poetry build` from the `viam-python-sdk` directory
4150
1. Find the newly created installable package located in `viam-python-sdk/dist/` and pip install it directly, e.g.: `pip install viam-python-sdk/dist/viam_sdk-0.1.0-py3-none-any.whl`
4251
1. Ensure that every connection has the option `disable_webrtc` set to `True`: `viam.rpc.dial.DialOptions(disable_webrtc=True)`
43-
* 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)
52+
- 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)
4453

4554
## Configure a client application at [app.viam.com](https://app.viam.com)
4655

@@ -79,31 +88,37 @@ The Viam Python SDK utilizes gRPC and, optionally WebRTC (defaults to on). gRPC
7988
Sessions are a safety feature that automatically cancel operations made by the python client if it loses connection to a robot. Sessions are enabled by default but can be disabled by setting `RobotClient.Options.disable_sessions = True`. Please see the [RDK session documentation](https://pkg.go.dev/go.viam.com/rdk/session) for more details and server-side configuration options.
8089
8190
## Examples
91+
8292
Read the [Example Usage](https://python.viam.dev/examples/example.html) page, to learn how to access a component, build a custom component, and expose
8393
custom components as a remote to existing robots.
8494
8595
More examples can be found in the [`examples`](/examples) directory.
8696
8797
## Documentation
98+
8899
Documentation, like this entire project, is under active development, and can be found at [python.viam.dev](https://python.viam.dev).
89100
90101
---
102+
91103
## Development
104+
92105
To contribute to the python SDK, please see the [contribution guidelines](https://python.viam.dev/contributing.html).
93106
94107
### Adding new resource types
108+
95109
The SDK provides a number of abstract base components and services (collectively: resources). To add more abstract resources, follow these steps:
96110
97111
1. Create a new directory in `viam.components` or `viam.services` with the name of the new component
98112
1. Create 4 new files in the newly created directory:
99-
1. Define all requirements of the resource in `{RESOURCE_NAME}.py`
100-
1. Implement the gRPC service for the new resource in `service.py`
101-
1. Create a gRPC client for the new resource in `client.py`
102-
1. Register the subtype and define package exports in `__init__.py`
113+
1. Define all requirements of the resource in `{RESOURCE_NAME}.py`
114+
1. Implement the gRPC service for the new resource in `service.py`
115+
1. Create a gRPC client for the new resource in `client.py`
116+
1. Register the subtype and define package exports in `__init__.py`
103117
1. Write tests for the new resource and add the resource to `tests.mocks.{components|services}`
104118
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`
105119
106120
## License
121+
107122
Copyright 2021-2023 Viam Inc.
108123
109124
Apache 2.0 - See [LICENSE](https://github.com/viamrobotics/viam-python-sdk/blob/main/LICENSE) file

SETUP.md

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# Viam + Python
2+
3+
This guide will help you get set up with Viam with Python. It assumes that you are starting from scratch, and will walk you through setting up a fresh environment and installing the necessary requirements.
4+
5+
## Setup your project
6+
7+
The first step is to create a directory to house your project. For this guide, we will be using the directory name `viam-python`:
8+
9+
```bash
10+
mkdir viam-python
11+
cd viam-python
12+
```
13+
14+
## Create a Virtual Environment
15+
16+
Now that we are in the project directory, let's create and activate a virtual environment for python to run in.
17+
18+
> **INFO**
19+
> Creating a virtual environment (`venv`) is important as it isolates this python environment from any other you might already have. This allows us to ensure a clean project and easier dependency management, and it avoids bloating your global python environment.
20+
21+
```bash
22+
python3 -m venv viam-env
23+
source viam-env/bin/activate
24+
```
25+
26+
> **INFO**
27+
> Some Linux environments may not have the necessary requirements to create a virtual environment. If you receive an error, you can try running `apt install python3-venv` and then running the above commands.
28+
29+
You will now see `(viam-env)` prepend the commands in your terminal window. This shows that the python packages being used are from this particular environment.
30+
31+
You can exit this environment by running `deactivate`.
32+
33+
## Install Viam
34+
35+
Inside the activated `viam-env` python environment, you can now install the Viam SDK:
36+
37+
```bash
38+
pip3 install viam-sdk
39+
```
40+
41+
This will install Viam and all required dependencies.
42+
43+
> **INFO**
44+
> Some features of the Viam SDK require additional dependencies. For example, the ML Models feature requires extras, which can be installed with `pip3 install viam-sdk[mlmodel]`. Read the documentation to determine if a specific feature requires extra dependencies.
45+
46+
Should you need to install your own requirements, be sure to do so in this environment.
47+
48+
## Setup your IDE
49+
50+
You'll now want to point your IDE to use the python interpreter of your new environment, rather than the default interpreter (likely the global python interpreter).
51+
52+
The following steps are for VS Code. If you're not using VS Code, please read your IDE's documentation on selecting python interpreters.
53+
54+
1. Open the `viam-python` directory in VS Code
55+
1. Open the Command Palette (using `⇧⌘P` or through the menus View -> Command Palette)
56+
1. Select the command `Python: Select Interpreter`. There, you should see all the interpreters available to you. You're looking for one the on you just made: `viam-env`. It will look something like: `Python 3.11.5 ('viam-env': venv) ./viam-env/bin/python`. If you don't see it, click the `Refresh` icon on the top right of the Command Palette.
57+
58+
Your IDE will now recognize all packages installed in this environment.
59+
60+
## Start building!
61+
62+
You are now ready to start using Viam's Python SDK!

docs/examples/example.ipynb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,9 @@
269269
"from typing import Any, Dict, Mapping, Optional\n",
270270
"\n",
271271
"from viam.components.sensor import Sensor\n",
272+
"from viam.logging import getLogger\n",
273+
"\n",
274+
"LOGGER = getLogger(__name__)\n",
272275
"\n",
273276
"\n",
274277
"class MySensor(Sensor):\n",
@@ -282,6 +285,11 @@
282285
" async def get_geometries(self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None) -> List[Geometry]:\n",
283286
" raise NotImplementedError\n",
284287
"\n",
288+
" def close(self):\n",
289+
" # This is a completely optional function to include. This will be called when the resource is removed from the config or the module\n",
290+
" # is shutting down.\n",
291+
" LOGGER.debug(f\"{self.name} is closed.\")\n",
292+
"\n",
285293
"# Anything below this line is optional and will be replaced later, but may come in handy for debugging and testing.\n",
286294
"# To use, call `python wifi_sensor_module.py` in the command line while in the `src` directory.\n",
287295
"async def main():\n",

docs/examples/module_step2.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,15 @@
44
from typing_extensions import Self
55

66
from viam.components.sensor import Sensor
7+
from viam.logging import getLogger
78
from viam.proto.app.robot import ComponentConfig
89
from viam.proto.common import ResourceName
910
from viam.resource.base import ResourceBase
1011
from viam.resource.registry import Registry, ResourceCreatorRegistration
1112
from viam.resource.types import Model, ModelFamily
1213

14+
LOGGER = getLogger(__name__)
15+
1316

1417
class MySensor(Sensor):
1518
# Subclass the Viam Sensor component and implement the required functions
@@ -26,6 +29,11 @@ async def get_readings(self, extra: Optional[Dict[str, Any]] = None, **kwargs) -
2629
wifi_signal = [x for x in content[2].split(" ") if x != ""]
2730
return {"link": wifi_signal[2], "level": wifi_signal[3], "noise": wifi_signal[4]}
2831

32+
def close(self):
33+
# This is a completely optional function to include. This will be called when the resource is removed from the config or the module
34+
# is shutting down.
35+
LOGGER.debug(f"{self.name} is closed.")
36+
2937

3038
async def main():
3139
Registry.register_resource_creator(Sensor.SUBTYPE, MySensor.MODEL, ResourceCreatorRegistration(MySensor.new))

docs/examples/module_step2_optional.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,15 @@
44
from typing_extensions import Self
55

66
from viam.components.sensor import Sensor
7+
from viam.logging import getLogger
78
from viam.proto.app.robot import ComponentConfig
89
from viam.proto.common import ResourceName
910
from viam.resource.base import ResourceBase
1011
from viam.resource.registry import Registry, ResourceCreatorRegistration
1112
from viam.resource.types import Model, ModelFamily
1213

14+
LOGGER = getLogger(__name__)
15+
1316

1417
class MySensor(Sensor):
1518
# Subclass the Viam Sensor component and implement the required functions
@@ -48,6 +51,11 @@ def reconfigure(self, config: ComponentConfig, dependencies: Mapping[ResourceNam
4851
multiplier = 1.0
4952
self.multiplier = multiplier
5053

54+
def close(self):
55+
# This is a completely optional function to include. This will be called when the resource is removed from the config or the module
56+
# is shutting down.
57+
LOGGER.debug(f"{self.name} is closed.")
58+
5159

5260
async def main():
5361
Registry.register_resource_creator(Sensor.SUBTYPE, MySensor.MODEL, ResourceCreatorRegistration(MySensor.new, MySensor.validate_config))

docs/examples/module_step3.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,16 @@
44
from typing_extensions import Self
55

66
from viam.components.sensor import Sensor
7+
from viam.logging import getLogger
78
from viam.module.module import Module
89
from viam.proto.app.robot import ComponentConfig
910
from viam.proto.common import ResourceName
1011
from viam.resource.base import ResourceBase
1112
from viam.resource.registry import Registry, ResourceCreatorRegistration
1213
from viam.resource.types import Model, ModelFamily
1314

15+
LOGGER = getLogger(__name__)
16+
1417

1518
class MySensor(Sensor):
1619
# Subclass the Viam Sensor component and implement the required functions
@@ -27,6 +30,11 @@ async def get_readings(self, extra: Optional[Dict[str, Any]] = None, **kwargs) -
2730
wifi_signal = [x for x in content[2].split(" ") if x != ""]
2831
return {"link": wifi_signal[2], "level": wifi_signal[3], "noise": wifi_signal[4]}
2932

33+
def close(self):
34+
# This is a completely optional function to include. This will be called when the resource is removed from the config or the module
35+
# is shutting down.
36+
LOGGER.debug(f"{self.name} is closed.")
37+
3038

3139
async def main():
3240
"""

docs/examples/my_cool_arm.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,12 @@
55
from typing import Any, Dict, List, Optional, Tuple
66

77
from viam.components.arm import Arm, JointPositions, KinematicsFileFormat, Pose
8+
from viam.logging import getLogger
89
from viam.operations import run_with_operation
910
from viam.proto.common import Capsule, Geometry, Sphere
1011

12+
LOGGER = getLogger(__name__)
13+
1114

1215
class MyCoolArm(Arm):
1316
# Subclass the Viam Arm component and implement the required functions
@@ -102,3 +105,8 @@ async def get_geometries(self, *, extra: Optional[Dict[str, Any]] = None, timeou
102105

103106
async def get_kinematics(self, extra: Optional[Dict[str, Any]] = None, **kwargs) -> Tuple[KinematicsFileFormat.ValueType, bytes]:
104107
return KinematicsFileFormat.KINEMATICS_FILE_FORMAT_SVA, self.kinematics
108+
109+
def close(self):
110+
# This is a completely optional function to include. This will be called when the resource is removed from the config or the module
111+
# is shutting down.
112+
LOGGER.debug(f"{self.name} is closed.")

examples/complex_module/src/arm/my_arm.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,15 @@
55
from typing_extensions import Self
66

77
from viam.components.arm import Arm, JointPositions, KinematicsFileFormat, Pose
8+
from viam.logging import getLogger
89
from viam.operations import run_with_operation
910
from viam.proto.app.robot import ComponentConfig
1011
from viam.proto.common import Capsule, Geometry, ResourceName, Sphere
1112
from viam.resource.base import ResourceBase
1213
from viam.resource.types import Model, ModelFamily
1314

15+
LOGGER = getLogger(__name__)
16+
1417

1518
class MyArm(Arm):
1619
# Subclass the Viam Arm component and implement the required functions
@@ -104,3 +107,8 @@ async def get_kinematics(self, extra: Optional[Dict[str, Any]] = None, **kwargs)
104107
with open(filepath, mode="rb") as f:
105108
file_data = f.read()
106109
return (KinematicsFileFormat.KINEMATICS_FILE_FORMAT_SVA, file_data)
110+
111+
def close(self):
112+
# This is a completely optional function to include. This will be called when the resource is removed from the config or the module
113+
# is shutting down.
114+
LOGGER.debug(f"{self.name} is closed.")

examples/complex_module/src/gizmo/my_gizmo.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
from typing_extensions import Self
44

5+
from viam.logging import getLogger
56
from viam.module.types import Reconfigurable
67
from viam.proto.app.robot import ComponentConfig
78
from viam.proto.common import ResourceName
@@ -10,6 +11,8 @@
1011

1112
from ..gizmo.api import Gizmo
1213

14+
LOGGER = getLogger(__name__)
15+
1316

1417
class MyGizmo(Gizmo, Reconfigurable):
1518
"""This is the specific implementation of a ``Gizmo`` (defined in api.py).
@@ -68,3 +71,8 @@ async def do_two(self, arg1: bool, **kwargs) -> str:
6871

6972
def reconfigure(self, config: ComponentConfig, dependencies: Mapping[ResourceName, ResourceBase]):
7073
self.my_arg = config.attributes.fields["arg1"].string_value
74+
75+
def close(self):
76+
# This is a completely optional function to include. This will be called when the resource is removed from the config or the module
77+
# is shutting down.
78+
LOGGER.debug(f"{self.name} is closed.")

examples/server/v1/components.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,9 @@ async def model_attributes(self) -> Board.Attributes:
338338
async def set_power_mode(self, **kwargs):
339339
raise NotImplementedError()
340340

341+
async def write_analog(self, pin: str, value: int, *, timeout: Optional[float] = None, **kwargs):
342+
raise NotImplementedError()
343+
341344
async def get_geometries(self, extra: Optional[Dict[str, Any]] = None, **kwargs) -> List[Geometry]:
342345
return GEOMETRIES
343346

examples/simple_module/src/main.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from typing_extensions import Self
55

66
from viam.components.sensor import Sensor
7+
from viam.logging import getLogger
78
from viam.module.module import Module
89
from viam.proto.app.robot import ComponentConfig
910
from viam.proto.common import ResourceName
@@ -12,6 +13,8 @@
1213
from viam.resource.types import Model, ModelFamily
1314
from viam.utils import ValueTypes
1415

16+
LOGGER = getLogger(__name__)
17+
1518

1619
class MySensor(Sensor):
1720
# Subclass the Viam Sensor component and implement the required functions
@@ -49,6 +52,11 @@ def reconfigure(self, config: ComponentConfig, dependencies: Mapping[ResourceNam
4952
multiplier = 1.0
5053
self.multiplier = multiplier
5154

55+
def close(self):
56+
# This is a completely optional function to include. This will be called when the resource is removed from the config or the module
57+
# is shutting down.
58+
LOGGER.debug(f"{self.name} is closed.")
59+
5260

5361
async def main():
5462
"""This function creates and starts a new module, after adding all desired resource models.

0 commit comments

Comments
 (0)