Skip to content

Commit e052e3c

Browse files
authored
DOCS-3172: Change references to old module generator to CLI (#3745)
1 parent e2cb006 commit e052e3c

File tree

3 files changed

+100
-121
lines changed

3 files changed

+100
-121
lines changed

docs/how-tos/create-module.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -163,9 +163,9 @@ In this step, you will code the logic that is unique to your model.
163163

164164
{{% alert title="Tip (optional)" color="tip" %}}
165165

166-
If you are using Golang, use the [Golang Module templates](https://github.com/viam-labs/module-templates-golang) which contain detailed instructions for creating your module.
166+
If you are using Python or Golang, use the [`viam module generate` CLI command](/cli/#module) to generate the scaffolding for a module with one resource model.
167167

168-
If you are using Python, you can use the [Viam module generator](https://github.com/viam-labs/generator-viam-module/) to generate the scaffolding for a module with one resource model.
168+
For a step-by-step guide that uses the CLI module generator, see [Create a Hello World module](/how-tos/hello-world-module/).
169169

170170
{{% /alert %}}
171171

docs/how-tos/hello-world-module.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -764,7 +764,7 @@ To package (for Python) and upload your module and make it available to configur
764764
{{< tabs >}}
765765
{{% tab name="Python" %}}
766766
767-
1. Package the module as an archive, run the following command from inside the <file>hello-world</file> directory:
767+
1. To package the module as an archive, run the following command from inside the <file>hello-world</file> directory:
768768
769769
```sh {id="terminal-prompt" class="command-line" data-prompt="$"}
770770
tar -czf module.tar.gz run.sh setup.sh requirements.txt src

docs/how-tos/sensor-module.md

+97-118
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,29 @@ Making a module to support your sensor will allow you to use it with Viam's data
2929
1. [Start with a test script](#start-with-a-test-script)
3030
1. [Generate template module code](#generate-template-module-code)
3131
1. [Implement the sensor API](#implement-the-sensor-api)
32-
1. [Make the module executable](#make-the-module-executable)
3332
1. [Test your module locally](#test-your-module-locally)
3433
1. [Upload your module](#upload-your-module-to-the-registry)
3534

3635
{{% /alert %}}
3736

37+
## Prerequisites
38+
39+
{{< expand "Install the Viam CLI and authenticate" >}}
40+
Install the Viam CLI and authenticate to Viam, from the same machine that you intend to upload your module from.
41+
42+
{{< readfile "/static/include/how-to/install-cli.md" >}}
43+
44+
Authenticate your CLI session with Viam using one of the following options:
45+
46+
{{< readfile "/static/include/how-to/auth-cli.md" >}}
47+
{{< /expand >}}
48+
49+
{{% expand "Install viam-server on your computer and connect to the Viam app" %}}
50+
51+
{{% snippet "setup.md" %}}
52+
53+
{{% /expand%}}
54+
3855
## Start with a test script
3956

4057
Start by getting a test script working so you can check that your sensor code itself works before packaging it into a module.
@@ -143,71 +160,28 @@ Run your test script from your terminal and make sure you are able to get readin
143160
There are a few standardized files that must be part of any module.
144161
You can create these automatically using the Viam module generator:
145162

146-
{{< table >}}
147-
{{% tablestep %}}
148-
**1. Install the module generator**
149-
150-
Install the [module generator](https://github.com/viam-labs/generator-viam-module/):
151-
152-
1. Install node (version 16 or later) and npm if you don't already have them:
153-
154-
```sh {id="terminal-prompt" class="command-line" data-prompt="$"}
155-
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.3/install.sh | bash
156-
nvm install 16
157-
nvm use 16
158-
```
159-
160-
1. Install Yeoman:
163+
1. Run the `module generate` command in your terminal:
161164

162165
```sh {id="terminal-prompt" class="command-line" data-prompt="$"}
163-
npm install -g yo
166+
viam module generate
164167
```
165168

166-
1. Now install the module generator:
167-
168-
```sh {id="terminal-prompt" class="command-line" data-prompt="$"}
169-
npm install -g generator-viam-module
170-
```
171-
172-
{{% /tablestep %}}
173-
{{% tablestep %}}
174-
**2. Run the generator**
175-
176-
Go to the directory where you want to create your module and run the generator:
177-
178-
```sh {id="terminal-prompt" class="command-line" data-prompt="$"}
179-
yo viam-module
180-
```
181-
182-
{{% /tablestep %}}
183-
{{% tablestep %}}
184-
**3. Name your model**
185-
186-
When prompted for a model triplet, use `<your organization public namespace>:<repo name>:<what you want to call your sensor model>`.
187-
For example, `jessamy:weather:meteo_PM`.
188-
189-
- You can find your organization namespace by going to your organization settings in the [Viam app](https://app.viam.com).
190-
- The repo name (also called family name) is generally the name of the GitHub repo where you will put your module code.
191-
Name it something related to what your module does.
192-
- Name your sensor 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.
193-
194-
For more information, see [Name your new resource model](/how-tos/create-module/#name-your-new-resource-model).
195-
196-
{{% /tablestep %}}
197-
{{% tablestep %}}
198-
**4. Specify the API**
199-
200-
For the API triplet, enter `rdk:component:sensor`.
201-
This means that you are implementing the standard Viam sensor API.
202-
203-
When asked whether this is a Viam SDK built-in API, enter `yes`.
169+
2. Follow the prompts, naming your module and selecting from the options.
204170

205-
{{% /tablestep %}}
206-
{{< /table >}}
207-
<br>
171+
<!--prettier-ignore-->
172+
| Prompt | Description |
173+
| -------| ----------- |
174+
| Module name | The module name describes the module or the family of devices it supports. It is generally the same as the name of the GitHub repo where you will put your module code. For example, `weather`. |
175+
| Language | The language for the module. To follow this guide, choose Python. |
176+
| Visibility | Choose `Private` to share only with your organization, or `Public` to share publicly with all organizations. If you are testing, choose `Private`. |
177+
| Namespace/Organization ID | In the [Viam app](https://app.viam.com), navigate to your organization settings through the menu in upper right corner of the page. Find the **Public namespace** and copy that string. In the example snippets below, the namespace is `jessamy`. |
178+
| Resource to add to the module (API) | The [component API](/appendix/apis/#component-apis) or [service API](/appendix/apis/#service-apis) the resource you're creating implements. Choose `Sensor Component` for this guide. |
179+
| Model name | Name your sensor 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. |
180+
| Enable cloud build | You can select `No` for this guide because you'll build the module yourself before uploading it. If you select `Yes` 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. |
181+
| Register module | Select `Yes` unless you are creating a local-only module for testing purposes and do not intend to upload it. |
208182

209-
The generator creates a `run.sh` file, a `requirements.txt` file, a readme, and source code files.
210-
In the next section, you'll customize some of these files to support your sensor.
183+
The generator will create a folder containing stub files for your modular sensor component.
184+
In the next section, you'll customize some of the generated files to support your sensor.
211185

212186
## Implement the sensor API
213187

@@ -218,18 +192,19 @@ You need to implement this method so your sensor supports the sensor API:
218192
{{% tablestep %}}
219193
**1. Edit configuration code**
220194

221-
In the generated <file>/YOUR_MODULE_NAME/src/</file> directory, open the </file>MODEL_NAME.py</file> file.
195+
In the generated <file>/YOUR_MODULE_NAME/src/</file> directory, open the </file>main.py</file> file.
222196

223197
Edit the config attributes to fit your sensor.
224-
For example, if your sensor requires two pins, copy the `some_pin` lines and add another pin with a different name.
225-
If you want to be able to configure something else, for example the location to get online data from, you can add attributes for that.
226-
If your sensor doesn't require any configuration, delete the `some_pin` lines but don't delete the `validate` and `reconfigure` functions entirely; they're needed for the module to function even if they don't actually validate the input or reconfigure the resource.
198+
For example, if your sensor requires two pins, edit the validate function to check that they are configured.
199+
Edit the reconfigure function to get the configured values of each parameter from the configuration.
200+
If you want to be able to configure something else, for example the location to get online data from, you can add attributes for that (see example code in the expander below).
201+
If your sensor doesn't require any configuration, leave the `validate` and `reconfigure` functions as they are; they're needed for the module to function even if they don't actually validate the input or reconfigure the resource.
227202

228203
{{% /tablestep %}}
229204
{{% tablestep %}}
230205
**2. Define `get_readings`**
231206

232-
In the `get_readings` function definition, paste your test script.
207+
In the `get_readings` function definition, replace `raise NotImplementedError()` by pasting your test script.
233208
Edit the script to return a dictionary of readings instead of printing them.
234209
Be sure to add any required imports to the top of the file.
235210

@@ -248,52 +223,45 @@ Be sure to add any required imports to the top of the file.
248223
The following code puts the functionality of the [example test script](#start-with-a-test-script) into the `get_readings` function definition.
249224

250225
```python {class="line-numbers linkable-line-numbers"}
251-
# meteo_PM.py
252-
from typing import ClassVar, Mapping, Any, Optional
253-
from typing_extensions import Self
226+
import asyncio
227+
from typing import Any, ClassVar, Final, Mapping, Optional, Sequence
254228

255-
from viam.utils import SensorReading, struct_to_dict
256-
from viam.module.types import Reconfigurable
229+
from typing_extensions import Self
230+
from viam.components.sensor import Sensor
231+
from viam.module.module import Module
257232
from viam.proto.app.robot import ComponentConfig
258233
from viam.proto.common import ResourceName
259234
from viam.resource.base import ResourceBase
235+
from viam.resource.easy_resource import EasyResource
260236
from viam.resource.types import Model, ModelFamily
261-
from viam.components.sensor import Sensor
262-
from viam.logging import getLogger
237+
from viam.utils import SensorReading, struct_to_dict
263238

264239
import openmeteo_requests
265240
import requests_cache
266241
from retry_requests import retry
267242

268-
LOGGER = getLogger(__name__)
269-
270-
271-
class meteo_PM(Sensor, Reconfigurable):
272-
273-
"""
274-
Sensor represents a sensing device that can provide measurement readings.
275-
"""
276243

244+
class Meteopm(Sensor, EasyResource):
277245
MODEL: ClassVar[Model] = Model(
278246
ModelFamily("jessamy", "weather"), "meteo_PM")
279247

280-
# Class parameters
281-
latitude: float # Latitude at which to get data
282-
longitude: float # Longitude at which to get data
283-
284-
# Constructor
285248
@classmethod
286249
def new(
287-
cls, config: ComponentConfig,
288-
dependencies: Mapping[ResourceName, ResourceBase]
289-
) -> Self:
290-
my_class = cls(config.name)
291-
my_class.reconfigure(config, dependencies)
292-
return my_class
293-
294-
# Validates JSON Configuration
250+
cls, config: ComponentConfig, dependencies: Mapping[
251+
ResourceName, ResourceBase]
252+
) -> Self:
253+
"""This method creates a new instance of this Sensor component.
254+
The default implementation sets the name from the `config` parameter
255+
and then calls `reconfigure`.
256+
"""
257+
return super().new(config, dependencies)
258+
295259
@classmethod
296-
def validate(cls, config: ComponentConfig):
260+
def validate_config(cls, config: ComponentConfig) -> Sequence[str]:
261+
"""This method allows you to validate the configuration object
262+
received from the machine, as well as to return any implicit
263+
dependencies based on that `config`.
264+
"""
297265
fields = config.attributes.fields
298266
# Check that configured fields are floats
299267
if "latitude" in fields:
@@ -303,28 +271,31 @@ class meteo_PM(Sensor, Reconfigurable):
303271
if "longitude" in fields:
304272
if not fields["longitude"].HasField("number_value"):
305273
raise Exception("Longitude must be a float.")
306-
return
274+
return []
307275

308-
# Handles attribute reconfiguration
309276
def reconfigure(
310-
self, config: ComponentConfig,
311-
dependencies: Mapping[ResourceName, ResourceBase]
312-
):
277+
self, config: ComponentConfig, dependencies: Mapping[
278+
ResourceName, ResourceBase]
279+
):
280+
"""This method allows you to dynamically update your service
281+
when it receives a new `config` object.
282+
"""
313283
attrs = struct_to_dict(config.attributes)
314284

315285
self.latitude = float(attrs.get("latitude", 45))
316-
LOGGER.debug("Using latitude: " + str(self.latitude))
286+
self.logger.debug("Using latitude: " + str(self.latitude))
317287

318288
self.longitude = float(attrs.get("longitude", -121))
319-
LOGGER.debug("Using longitude: " + str(self.longitude))
320-
321-
return
289+
self.logger.debug("Using longitude: " + str(self.longitude))
290+
return super().reconfigure(config, dependencies)
322291

323292
async def get_readings(
324-
self, *, extra: Optional[Mapping[str, Any]] = None,
325-
timeout: Optional[float] = None, **kwargs
293+
self,
294+
*,
295+
extra: Optional[Mapping[str, Any]] = None,
296+
timeout: Optional[float] = None,
297+
**kwargs
326298
) -> Mapping[str, SensorReading]:
327-
328299
# Set up the Open-Meteo API client with cache and retry on error
329300
cache_session = requests_cache.CachedSession(
330301
'.cache', expire_after=3600)
@@ -351,13 +322,17 @@ class meteo_PM(Sensor, Reconfigurable):
351322
current_pm10 = current.Variables(0).Value()
352323
current_pm2_5 = current.Variables(1).Value()
353324

354-
LOGGER.info(current_pm2_5)
325+
self.logger.info(current_pm2_5)
355326

356327
# Return a dictionary of the readings
357328
return {
358329
"pm2_5": current_pm2_5,
359330
"pm10": current_pm10
360331
}
332+
333+
334+
if __name__ == "__main__":
335+
asyncio.run(Module.run_from_registry())
361336
```
362337

363338
{{< /expand >}}
@@ -370,15 +345,6 @@ Most modules have their implementation code linked on their module page, so you
370345
Update the generated <file>requirements.txt</file> file to include any packages that must be installed for the module to run.
371346
Depending on your use case, you may not need to add anything here beyond `viam-sdk` which is auto-populated.
372347

373-
## Make the module executable
374-
375-
You need an executable file so that `viam-server` can run your module.
376-
The module generator already created the <file>run.sh</file> "entrypoint" file for you, so all you need to do is make this file executable by running the following command with your correct file path:
377-
378-
```sh {id="terminal-prompt" class="command-line" data-prompt="$"}
379-
sudo chmod +x <your-file-path-to>/run.sh
380-
```
381-
382348
## Test your module locally
383349

384350
{{% expand "Prerequisite: A running machine connected to the Viam app." %}}
@@ -394,7 +360,20 @@ It's a good idea to test your module locally before uploading it to the [Viam Re
394360

395361
{{< table >}}
396362
{{% tablestep link="/how-tos/create-module/#test-your-module-locally" %}}
397-
**1. Configure your local module on a machine**
363+
**1. Set up a virtual environment**
364+
365+
Create a virtual Python environment with the necessary packages by running the setup file from within the <file>hello-world</file> directory:
366+
367+
```sh {id="terminal-prompt" class="command-line" data-prompt="$"}
368+
sh setup.sh
369+
```
370+
371+
This environment is where the local module will run.
372+
`viam-server` does not need to run inside this environment.
373+
374+
{{% /tablestep %}}
375+
{{% tablestep %}}
376+
**2. Configure your local module on a machine**
398377

399378
On your machine's **CONFIGURE** tab in the [Viam app](https://app.viam.com), click the **+** (create) icon in the left-hand menu.
400379
Select **Local module**, then **Local module**.
@@ -403,7 +382,7 @@ Type in the _absolute_ path on your machine's filesystem to your module's execut
403382
Click **Create**.
404383

405384
{{% /tablestep %}}
406-
{{% tablestep link="/how-tos/create-module/#test-your-module-locally" %}}
385+
{{% tablestep %}}
407386
**2. Configure the model provided by your module**
408387

409388
Click the **+** button again, this time selecting **Local module** and then **Local component**.

0 commit comments

Comments
 (0)