Deploy software packages to machines (OTA)

To deploy code or other software to individual machines:

  1. Create a module with machine control logic
  2. Create a fragment with the configuration for your machines
  3. Add the fragment to your machines

Prerequisites

This guide assumes that you have at least one machine with configured hardware and software resources.

Create a module with machine control logic

You can operate devices by running control logic on another device, for example using an app, or on the machine itself using a module. If all your machine control logic runs on another device or server, move on to Create a fragment.

If at least some of your machine control logic should run on your machine, place the control logic in a module by following these steps:

1. Generate stub files

Run the module generate command in your terminal:

viam module generate --resource-subtype=generic-component

Follow the prompts, selecting the following options:

  • Module name: Your choice, for example my-control-logic
  • Language: Your choice
  • Visibility: Private
  • Namespace/Organization ID: In the Viam app, 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 naomi.
  • Resource to be added to the module: Generic Component.1
  • Model name: Your choice, for example control-logic
  • Enable cloud build: Choose Yes if you are using GitHub or want to use cloud build.
  • Register module: Yes

Press the Enter key and the generator will create a folder for your control logic component.


  1. For simplicity, this guide uses the generic component. You can choose a different resource type to add your control logic to. For example, for logic controlling a camera, you may wish to use the camera component. You must implement any required API methods for the chosen component. ↩︎

2. Add your control logic

When your new model gets added to your machine, its reconfigure() method gets called. If you want your control logic to run in a loop in the background, you can start this loop here. Be sure to also implement logic to handle subsequent calls to the reconfigure method gracefully.

For example, in Python, start your logic in src/main.py:

# Add these imports
import asyncio
from threading import Event
from viam.logging import getLogger


LOGGER = getLogger("control-logic")


class ControlLogic(Generic, EasyResource):
    MODEL: ClassVar[Model] = Model(
        ModelFamily("naomi", "my-control-logic"), "control-logic"
    )
    running = None
    task = None
    event = Event()

    # Other methods omitted for brevity

    def reconfigure(
        self, config: ComponentConfig,
        dependencies: Mapping[ResourceName, ResourceBase]
    ):
        # starts automatically
        if self.running is None:
            self.start()
        else:
            LOGGER.info("Already running control logic.")

    def start(self):
        loop = asyncio.get_event_loop()
        self.task = loop.create_task(self.control_loop())
        self.event.clear()

    def stop(self):
        self.event.set()
        if self.task is not None:
            self.task.cancel()

    async def control_loop(self):
        while not self.event.is_set():
            await self.on_loop()
            await asyncio.sleep(0)

    async def on_loop(self):
        try:
            LOGGER.info("Executing control logic")
            # TODO: ADD CONTROL LOGIC

        except Exception as err:
            LOGGER.error(err)
        await asyncio.sleep(1)

    def __del__(self):
        self.stop()

    async def close(self):
        self.stop()

    async def do_command(
        self,
        command: Mapping[str, ValueTypes],
        *,
        timeout: Optional[float] = None,
        **kwargs
    ) -> Mapping[str, ValueTypes]:
        result = {key: False for key in command.keys()}
        for name, _args in command.items():
            if name == "start":
                self.start()
                result[name] = True
            if name == "stop":
                self.stop()
                result[name] = True
        return result


if __name__ == "__main__":
    asyncio.run(Module.run_from_registry())

For complete examples that implement control logic, see:

3. Package your control logic

Once you have implemented your control logic, commit and push your changes to a GitHub repository.

If you are not using GitHub, see Upload your module and Update an existing module for more information on alternatives.

Follow the steps in Upload your module using cloud build.

Then create a new release with a tag of the form 1.0.0. Your module will now be built, packaged and pushed to the Viam Registry.

Create a fragment

Viam has a built-in tool called fragments for using the same configuration on multiple machines. When deploying or updating software on many machines, you should use fragments.

The following example creates a fragment with a camera and a servo, as well as with the control logic module.

1. Configure your software

Go to your machine in the Viam app. Ensure all hardware and other resources your machine uses are configured.

Add your control logic module.

Configuration builder UI

2. Set the version and update strategy

Scroll to the module card for your control logic module and select the pinned version type. You can select a specific version or set the machine to always update to the latest major, minor, patch, or pre-release version once new versions are available. For more information on these configuration options, see Module versioning.

Module card UI

3. Copy the raw JSON

In your machine’s CONFIGURE tab, switch to JSON and copy the raw JSON.

The following example shows a machine with a configured camera and the control logic module. Your machine will have different resources.

Configuration builder UI

4. Create a fragment

Go to app.viam.com/fragments.

Add a fragment, and paste the copied JSON configuration into it.

Configuration builder UI

Set your privacy settings. There are three options for this:

  • Public: Any user inside or outside of your organization will be able to view and use this fragment.
  • Private: No user outside of your organization will be able to view or use this fragment.
  • Unlisted: Any user inside or outside of your organization, with a direct link, will be able to view and use this fragment.

Click Save.

If you want to edit the fragment later, do it from this screen.

Delete

5. Delete the original machine configuration (optional)

Now that the configuration is saved as a fragment, you can delete the machine you created in step 1. We only created this machine to easily generate the JSON config for the fragment.

Add the fragment to your machines

Generally, you will use provisioning to add this fragment to your machines.

You can also add the fragment manually to the machines that need it:

Add fragment

Add the fragment to one machine

On your machine’s CONFIGURE tab, click the + button and select Insert fragment. Search for your fragment and add it.

Click Save in the upper right corner of the screen.