Skip to content

Commit a075d89

Browse files
committed
More testing
1 parent bd146c4 commit a075d89

File tree

1 file changed

+78
-64
lines changed
  • docs/operate/modules/support-hardware

1 file changed

+78
-64
lines changed

docs/operate/modules/support-hardware/_index.md

Lines changed: 78 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -37,19 +37,19 @@ aliases:
3737
- /operate/modules/supported-hardware/hello-world-module/
3838
---
3939

40-
If your physical or virtual hardware is not [already supported](/operate/modules/configure-modules/) by an existing {{< glossary_tooltip term_id="module" text="module" >}}, you can create a new module to add support for it.
40+
If your physical or virtual hardware is not supported by an existing registry {{< glossary_tooltip term_id="module" text="module" >}}, you can create a new module to add support for it.
4141

4242
{{% hiddencontent %}}
4343
If you want to create a "custom module", this page provides instructions for creating one in Python and Go.
4444
{{% /hiddencontent %}}
4545

46-
This page provides instructions for creating and uploading a module in Python or Go.
46+
This page provides instructions for creating a module in Python or Go.
4747
For C++ module examples, see the [C++ examples directory on GitHub](https://github.com/viamrobotics/viam-cpp-sdk/tree/main/src/viam/examples/).
4848
If you want to create a module for use with a microcontroller, see [Modules for ESP32](/operate/modules/advanced/micro-module/).
4949

5050
**Example module:** With each step of thise guide, you have instruction for creating a {{< glossary_tooltip term_id="module" text="module" >}} which does two things:
5151

52-
1. Opens an image file from a configured path on your machine
52+
1. Gets an image from a configured path on your machine
5353
2. Returns a random number
5454

5555
## Prerequisites
@@ -67,7 +67,6 @@ Authenticate your CLI session with Viam using one of the following options:
6767
{{% expand "A running machine connected to Viam." %}}
6868

6969
You can write a module without a machine, but to test your module you'll need a [machine](/operate/install/setup/).
70-
Make sure to physically connect your sensor to your machine's computer to prepare your machine for testing.
7170

7271
{{% snippet "setup.md" %}}
7372

@@ -76,6 +75,7 @@ Make sure to physically connect your sensor to your machine's computer to prepar
7675
{{< expand "For Python developers: Use Python 3.11+" >}}
7776

7877
If you plan to write your module using Python, you need Python version 3.11 or newer installed on your computer to use the code generation tool in this guide.
78+
7979
You can check by running `python3 --version` or `python --version` in your terminal.
8080

8181
{{< /expand >}}
@@ -84,7 +84,7 @@ You can check by running `python3 --version` or `python --version` in your termi
8484

8585
While not required, we recommend starting by writing a test script to check that you can connect to and control your hardware from your computer, perhaps using the manufacturer's API or other low-level code.
8686

87-
**Example module:** For the example module the test script will test how to open an image in the same folder and how to print a random number.
87+
**Example module:** For the example module the test script will open an image in the same folder and print a random number.
8888

8989
{{< tabs >}}
9090
{{% tab name="Python" %}}
@@ -105,7 +105,7 @@ print(random_number)
105105
{{% /tab %}}
106106
{{% tab name="Go" %}}
107107

108-
```go {class="line-numbers linkable-line-numbers" data-start="1" }
108+
```go {class="line-numbers linkable-line-numbers" data-start="1" }
109109
package main
110110

111111
import (
@@ -145,11 +145,6 @@ The module takes the functionality of the script and maps it to a standardized A
145145

146146
Review the available [component APIs](/dev/reference/apis/#component-apis) and choose the one whose methods map most closely to the functionality you need.
147147

148-
For example, if you just need to get readings or other data and don't need any other endpoints, you could use the [sensor API](/dev/reference/apis/components/sensor/), which contains only the `GetReadings` method (as well as the methods that all Viam resources implement: `Reconfigure`, `DoCommand`, `GetResourceName`, and `Close`).
149-
150-
You do not need to fully implement all the methods of an API.
151-
For example, if you want to use the [camera API](/dev/reference/apis/components/camera/) because you want to return images, but your camera does not get point cloud data, you can implement the `GetImage` method but for the `GetPointCloud` method you can return an "unimplemented" error.
152-
153148
If you need a method that is not in your chosen API, you can use the flexible `DoCommand` (which is built into all component APIs) to create custom commands.
154149
See [Run control logic](/docs/operate/modules/support-hardware/) for more information.
155150

@@ -159,8 +154,12 @@ You need a way to return an image and you need a way to return a number.
159154
If you look at the [camera API](/dev/reference/apis/components/camera/), you can see the `GetImage` method, which returns an image.
160155
That will work for the image.
161156

157+
The camera API also has a few other methods.
158+
You do not need to fully implement all the methods of an API.
159+
For example, this camera does not use point cloud data, so for methods like `GetPointCloud` it will return an "unimplemented" error.
160+
162161
The [sensor API](/dev/reference/apis/components/sensor/) includes the `GetReadings` method.
163-
You can return a number with that
162+
You can return the random number with that.
164163

165164
Note that the camera API can't return a number and the sensor API can't return an image.
166165
Each model can implement only one API, but your module can contain multiple modular resources.
@@ -189,10 +188,10 @@ viam module generate
189188
| Module name | Choose a name that describes the set of {{< glossary_tooltip term_id="resource" text="resources" >}} it supports. |
190189
| Language | Choose the programming language for the module. The CLI supports `Python` and `Golang`. |
191190
| Visibility | Choose `Private` to share only with your organization, or `Public` to share publicly with all organizations. If you are testing, choose `Private`. |
192-
| Namespace/Organization ID | Navigate to your organization settings through the menu in upper right corner of the page. Find the **Public namespace** (or create one if you haven't already) and copy that string. If you use the organization ID, you must still create a public namespace first. |
193-
| Resource to add to the module (API) | The [component API](/dev/reference/apis/#component-apis) your module will implement. See [How to design your module](./#how-to-design-your-module) for more information. |
191+
| Namespace/Organization ID | Navigate to your organization settings through the menu in upper right corner of the page. Find the **Public namespace** (or create one if you haven't already) and copy that string. If you use the organization ID, you must still create a public namespace first if you wish to share the module publicly. |
192+
| Resource to add to the module (API) | The [component API](/dev/reference/apis/#component-apis) your module will implement. See [Choose an API](#choose-an-api) for more information. |
194193
| Model name | Name your component model based on what it supports, for example, if it supports a model of ultrasonic sensor called "XYZ Sensor 1234" you could call your model `xyz_1234` or similar. Must be all-lowercase and use only alphanumeric characters (`a-z` and `0-9`), hyphens (`-`), and underscores (`_`). |
195-
| Enable cloud build | If you select `Yes` (recommended) and push the generated files (including the <file>.github</file> folder) and create a release of the format `vX.X.X`, the module will build and upload to the Viam registry and be available for all Viam-supported architectures without you needing to build for each architecture. `Yes` also makes it easier to [upload](/operate/modules/deploy-module/) using PyInstaller by creating a build entrypoint script. You can select `No` if you want to always build the module yourself before uploading it. Fort more information see [Update and manage modules](/operate/modules/advanced/manage-modules/). |
194+
| Enable cloud build | If you select `Yes` (recommended) and push the generated files (including the <file>.github</file> folder) and create a release of the format `X.X.X`, the module will build for [all architectures specified in the meta.json build file](/operate/modules/advanced/metajson/). You can select `No` if you want to always build the module yourself before uploading it. For more information see [Update and manage modules](/operate/modules/advanced/manage-modules/). |
196195
| Register module | Select `Yes` unless you are creating a local-only module for testing purposes and do not intend to upload it. Registering a module makes its name and metadata appear in the registry; uploading the actual code that powers the module is a separate step. If you decline to register the module at this point, you can run [`viam module create`](/dev/tools/cli/#module) to register it later. |
197196

198197
{{% /expand %}}
@@ -250,6 +249,15 @@ hello-world/
250249
└── setup.sh
251250
```
252251

252+
If you want to understand the module structure, here's what each file does:
253+
254+
- **<FILE>README.md</FILE>**: Documentation template that gets uploaded to the registry when you upload the module.
255+
- **<FILE>meta.json</FILE>**: Module metadata that gets uploaded to the registry when you upload the module.
256+
- **<FILE>main.py</FILE>** and **<FILE>hello_camera.py</FILE>**: Core code that registers the module and resource and provides the model implementation.
257+
- **<FILE>setup.sh</FILE>** and **<FILE>requirements.txt</FILE>**: Setup script that creates a virtual environment and installs the dependencies listed in <FILE>requirements.txt</FILE>.
258+
- **<FILE>build.sh</FILE>**: Build script that packages the code for upload.
259+
- **<FILE>run.sh</FILE>**: Script that runs <FILE>setup.sh</FILE> and then executes the module from <FILE>main.py</FILE>.
260+
253261
{{% /tab %}}
254262
{{% tab name="Go" %}}
255263

@@ -267,6 +275,12 @@ hello-world/
267275
└── meta.json
268276
```
269277

278+
If you want to understand the module structure, here's what each file does:
279+
280+
- **<FILE>README.md</FILE>**: Documentation template that gets uploaded to the registry when you upload the module.
281+
- **<FILE>meta.json</FILE>**: Module metadata that gets uploaded to the registry when you upload the module.
282+
- TODO
283+
270284
{{% /tab %}}
271285
{{< /tabs >}}
272286

@@ -456,7 +470,6 @@ Save the file.
456470
{{% /tab %}}
457471
{{< /tabs >}}
458472

459-
460473
You can now delete the temporary <file>hello-world/hello-world</file> folder and all its contents.
461474

462475
### Implement the components
@@ -472,11 +485,11 @@ When logged in you can also download the module's source code to inspect it.
472485

473486
Generally you will add your custom logic in these files:
474487

475-
| File | Description |
476-
| ---- | ----------- |
477-
| <file>/src/models/&lt;model-name&gt;.py</file> | Set up the configuration options for the model and implement the API methods for the model. |
478-
| `setup.sh` and `run.sh` | Add any logic for installing or running other software for your module. |
479-
| `requirements.txt` | Add any python packages that are required for your module. They will be installed by `setup.sh`. |
488+
| File | Description |
489+
| ---------------------------------------------- | ------------------------------------------------------------------------------------------------ |
490+
| <file>/src/models/&lt;model-name&gt;.py</file> | Set up the configuration options for the model and implement the API methods for the model. |
491+
| `setup.sh` and `run.sh` | Add any logic for installing or running other software for your module. |
492+
| `requirements.txt` | Add any python packages that are required for your module. They will be installed by `setup.sh`. |
480493

481494
<br>
482495

@@ -485,8 +498,8 @@ Generally you will add your custom logic in these files:
485498

486499
Generally you will add your custom logic in these files:
487500

488-
| File | Description |
489-
| ---- | ----------- |
501+
| File | Description |
502+
| ------------------------------------------ | ---------------------------------------- |
490503
| Model file (for example `hello-camera.go`) | Implement the API methods for the model. |
491504

492505
{{% /tab %}}
@@ -511,10 +524,10 @@ Model configuration happens in two steps:
511524

512525
The validation step serves two purposes:
513526

514-
- Confirm that the model configuration contains all **required attributes** and that these attributes are of the right type.
515-
- Identify and return a list of names of **required resources** and a list of names of **optional resources**.
516-
`viam-server` will pass these resources to the next step as dependencies.
517-
For more information, see [Module dependencies](/operate/modules/advanced/dependencies/).
527+
- Confirm that the model configuration contains all **required attributes** and that these attributes are of the right type.
528+
- Identify and return a list of names of **required resources** and a list of names of **optional resources**.
529+
`viam-server` will pass these resources to the next step as dependencies.
530+
For more information, see [Module dependencies](/operate/modules/advanced/dependencies/).
518531

519532
**Example module**: Imagine how a user might configure the finished camera model.
520533
Since the camera model returns an image at a provided path, the configuration must contain a variable to pass in the file path.
@@ -596,21 +609,21 @@ The validation step serves two purposes:
596609

597610
2. Edit the `reconfigure` function to:
598611

599-
```python {class="line-numbers" data-start="48" data-line="4-5"}
600-
def reconfigure(
601-
self, config: ComponentConfig, dependencies: Mapping[ResourceName, ResourceBase]
602-
):
603-
attrs = struct_to_dict(config.attributes)
604-
self.image_path = str(attrs.get("image_path"))
612+
```python {class="line-numbers" data-start="51" data-line="4-5"}
613+
def reconfigure(
614+
self, config: ComponentConfig, dependencies: Mapping[ResourceName, ResourceBase]
615+
):
616+
attrs = struct_to_dict(config.attributes)
617+
self.image_path = str(attrs.get("image_path"))
605618

606-
return super().reconfigure(config, dependencies)
607-
```
619+
return super().reconfigure(config, dependencies)
620+
```
608621

609622
3. Add the following import to the top of the file:
610623

611-
```python {class="line-numbers" data-start="1"}
612-
from viam.utils import struct_to_dict
613-
```
624+
```python {class="line-numbers" data-start="1"}
625+
from viam.utils import struct_to_dict
626+
```
614627

615628
{{% /tab %}}
616629
{{% tab name="Go" %}}
@@ -680,21 +693,24 @@ You can find details about the return types at [python.viam.dev](https://python.
680693
{{< table >}}
681694
{{< tablestep start=1 >}}
682695

683-
The module generator created a stub for the `get_image()` function we want to implement in <file>hello-world/src/models/hello_camera.py</file>.
696+
The module generator created a stub for the `get_images()` function we want to implement in <file>hello-world/src/models/hello_camera.py</file>.
684697

685698
You need to replace `raise NotImplementedError()` with code to implement the method:
686699

687-
```python {class="line-numbers linkable-line-numbers" data-start="60" data-line="9-10" }
688-
async def get_image(
689-
self,
690-
mime_type: str = "",
691-
*,
692-
extra: Optional[Dict[str, Any]] = None,
693-
timeout: Optional[float] = None,
694-
**kwargs
695-
) -> ViamImage:
696-
img = Image.open(self.image_path)
697-
return pil_to_viam_image(img, CameraMimeType.JPEG)
700+
```python {class="line-numbers linkable-line-numbers" data-start="74" data-line="9-10" }
701+
async def get_images(
702+
self,
703+
*,
704+
filter_source_names: Optional[Sequence[str]] = None,
705+
extra: Optional[Dict[str, Any]] = None,
706+
timeout: Optional[float] = None,
707+
**kwargs
708+
) -> Tuple[Sequence[NamedImage], ResponseMetadata]:
709+
img = Image.open(self.image_path)
710+
vi_img = pil_to_viam_image(img, CameraMimeType.JPEG)
711+
named = NamedImage("default", vi_img.data, vi_img.mime_type)
712+
metadata = ResponseMetadata()
713+
return [named], metadata
698714
```
699715

700716
Add the following import to the top of the file:
@@ -891,12 +907,13 @@ You can configure it in the web UI using the local files on your machine.
891907

892908
### Add module to machine
893909

894-
If you enabled cloud build, you can use hot reloading, which will package the module and copy it to a machine for testing.
910+
To get your module onto your machine, hot reloading will build and package it and then use the shell service to copy it to the machine for testing.
911+
If you are using Python `venv`, make sure your module files are on the same device where `viam-server` is running and manually add the module instead.
895912

896913
{{< tabs >}}
897914
{{% tab name="Hot reloading (recommended)" %}}
898915

899-
Run the following command to build and start your module and push it to your machine:
916+
Run the following command to build the module and add it to your machine:
900917

901918
{{< tabs >}}
902919
{{% tab name="Same device" %}}
@@ -915,21 +932,21 @@ viam module reload --part-id 123abc45-1234-432c-aabc-z1y111x23a00
915932
{{% /tab %}}
916933
{{< /tabs >}}
917934

918-
For more information, see the [CLI documentation](/dev/tools/cli/#module).
935+
For more information, see the [`viam module` documentation](/dev/tools/cli/#module).
919936

920937
{{< expand "Reload troubleshooting" >}}
921938

922-
`Error: Could not connect to machine part: context deadline exceeded; context deadline exceeded; mDNS query failed to find a candidate`
939+
- `Error: Could not connect to machine part: context deadline exceeded; context deadline exceeded; mDNS query failed to find a candidate`
923940

924-
- Try specifying the `--part-id`, which you can find by clicking the **Live** indicator on your machine's page and clicking **Part ID**.
941+
Try specifying the `--part-id`, which you can find by clicking the **Live** indicator on your machine's page and clicking **Part ID**.
925942

926-
`Error: Rpc error: code = Unknown desc = stat /root/.viam/packages-local: no such file or directory`
943+
- `Error: Rpc error: code = Unknown desc = stat /root/.viam/packages-local: no such file or directory`
927944

928-
- Try specifying the `--home` directory, for example `/Users/yourname/` on macOS.
945+
Try specifying the `--home` directory, for example `/Users/yourname/` on macOS.
929946

930-
`Error: Error while refreshing token, logging out. Please log in again`
947+
- `Error: Error while refreshing token, logging out. Please log in again`
931948

932-
- Run `viam login` to reauthenticate the CLI.
949+
Run `viam login` to reauthenticate the CLI.
933950

934951
### Try using a different command
935952

@@ -947,10 +964,8 @@ In upper right corner of the module's card, click the **...** menu, then click *
947964

948965
{{< /expand >}}
949966

950-
When you run `viam module reload`, the module will be added to your device automatically.
951-
952967
{{% /tab %}}
953-
{{% tab name="Manual" %}}
968+
{{% tab name="Manual (required for Python venv)" %}}
954969

955970
Navigate to your machine's **CONFIGURE** page.
956971

@@ -985,7 +1000,6 @@ For the `hello-world` module, the path should resemble `/home/yourname/hello-wor
9851000

9861001
Click **Create**.
9871002

988-
9891003
{{% /tab %}}
9901004
{{< /tabs >}}
9911005

@@ -1077,7 +1091,7 @@ viam module reload --part-id 123abc45-1234-432c-aabc-z1y111x23a00
10771091
{{< /tabs >}}
10781092

10791093
{{% /tab %}}
1080-
{{% tab name="Manual" %}}
1094+
{{% tab name="Manual (required for Python venv)" %}}
10811095

10821096
{{< tabs >}}
10831097
{{% tab name="Python" %}}

0 commit comments

Comments
 (0)