diff --git a/docs/build-apps/concepts/how-apps-connect.md b/docs/build-apps/concepts/how-apps-connect.md
index 21340d39eb..4e36a1c719 100644
--- a/docs/build-apps/concepts/how-apps-connect.md
+++ b/docs/build-apps/concepts/how-apps-connect.md
@@ -6,6 +6,8 @@ layout: "docs"
type: "docs"
description: "The transport paths the SDK uses, the session safety mechanism, and what the SDK reconnects automatically."
date: "2026-04-10"
+aliases:
+ - /operate/reference/sessions/
---
A Viam client application needs to reach a machine that may be on a different network, behind a NAT, or both. This page describes the transport paths the SDK uses, the session safety mechanism, and how the SDK reconnects when the network drops.
diff --git a/docs/build-apps/overview.md b/docs/build-apps/overview.md
index f0349089ed..2b21293dd2 100644
--- a/docs/build-apps/overview.md
+++ b/docs/build-apps/overview.md
@@ -7,6 +7,7 @@ type: "docs"
description: "Build software that uses a Viam SDK to talk to your machines and the Viam cloud, from web dashboards to long-running backend services."
date: "2026-04-10"
aliases:
+ - /operate/control/
- /operate/control/headless-app/
- /product-overviews/sdk-as-client/
- /program/sdk-as-client/
diff --git a/docs/build-modules/advanced-patterns.md b/docs/build-modules/advanced-patterns.md
new file mode 100644
index 0000000000..c623191a28
--- /dev/null
+++ b/docs/build-modules/advanced-patterns.md
@@ -0,0 +1,175 @@
+---
+linkTitle: "Advanced patterns"
+title: "Advanced module patterns"
+weight: 38
+layout: "docs"
+type: "docs"
+description: "Define new resource APIs, deploy custom components as remote parts, and package modules with Docker."
+date: "2026-04-20"
+aliases:
+ - /operate/reference/advanced-modules/
+ - /operate/reference/advanced-modules/create-subtype/
+ - /operate/reference/advanced-modules/custom-components-remotes/
+ - /operate/reference/advanced-modules/docker-modules/
+ - /program/extend/
+ - /modular-resources/advanced/
+ - /registry/advanced/
+ - /extend/modular-resources/create/create-subtype/
+ - /modular-resources/advanced/create-subtype/
+ - /registry/advanced/create-subtype/
+ - /program/extend/sdk-as-server/
+ - /program/extend/custom-components-remotes/
+ - /extend/custom-components-remotes/
+ - /modular-resources/advanced/custom-components-remotes/
+ - /registry/advanced/custom-components-remotes/
+---
+
+Some use cases require approaches beyond the standard module workflow. This page covers three advanced patterns: defining a new resource API, deploying custom components as remote parts, and packaging modules with Docker.
+
+## Define a new resource API
+
+You can define a new, custom resource API if:
+
+- You have a resource that does not fit into any of the existing [component](/reference/apis/#component-apis) or [service](/reference/apis/#service-apis) APIs.
+- You have a resource that could fit into an existing API, but you want different methods and messages.
+
+{{% alert title="Tip" color="tip" %}}
+
+Defining a new resource API is significantly more complex than using an existing API. In most cases, use an existing API instead.
+
+If you want to use most of an existing API but need a few additional functions, use the `DoCommand` endpoint with [extra parameters](/reference/sdks/use-extra-params/) to add custom functionality.
+
+If your use case uses only `DoCommand` and no other API methods, define a new model of [generic component](/reference/components/generic/) or [generic service](/reference/services/generic/).
+
+{{% /alert %}}
+
+### Steps to define a new API
+
+Viam uses [protocol buffers](https://protobuf.dev/) for API definition. To define a new API:
+
+1. Decide whether your custom API is a component (interfaces with hardware) or a service (provides higher-level functionality).
+
+2. Choose a name for your API (called the subtype). Determine a valid API namespace triplet. For example, `your-org-namespace:component:gizmo`.
+
+3. Create a directory for your module with a `src` subdirectory.
+
+ {{% alert title="Tip" color="tip" %}}
+ If you are writing your module in Python, you can use this [module generator tool](https://github.com/viam-labs/generator-viam-module) to generate stub files for the new API and a module that implements it.
+ {{% /alert %}}
+
+4. Write the proto methods in a `.proto` file inside `src/proto/`. For reference:
+
+ - [Example modular component proto file](https://github.com/viamrobotics/viam-python-sdk/blob/main/examples/complex_module/src/proto/gizmo.proto)
+ - [Example modular service proto file](https://github.com/viam-labs/speech-service-api/blob/main/src/proto/speech.proto)
+ - [Built-in Viam resource proto files](https://github.com/viamrobotics/api/tree/main/proto/viam)
+
+5. Define the proto methods in Python or Go in a file called `api.py` or `api.go`:
+
+ - [Example component in Python](https://github.com/viamrobotics/viam-python-sdk/blob/main/examples/complex_module/src/gizmo/api.py)
+ - [Example service in Python](https://github.com/viam-labs/speech-service-api/blob/main/src/speech_service_api/api.py)
+
+6. Generate the required configuration files (`buf.yaml`, `buf.gen.yaml`, `buf.lock`). See the [Buf documentation](https://buf.build/docs/generate/usage/).
+
+7. Use the protobuf compiler to [generate](https://buf.build/docs/tutorials/getting-started-with-buf-cli/#generate-code) all other necessary protocol buffer code from your `.proto` file.
+
+### After defining your API
+
+Once your API is defined, [create a model that implements it](/build-modules/write-a-driver-module/).
+
+Keep in mind:
+
+- You cannot use [SDKs](/reference/sdks/) to call your new API unless you build out the client to support it. Write code against the API in the language you used to define it.
+- You need a local copy of the module code on whatever machine runs client code against it.
+- Import the API definition from the module directory. For example, in Python: `from path.to.module.src.gizmo import Gizmo`.
+
+## Custom components as remote parts
+
+Running modular resources on the computer directly connected to your components is the preferred approach. However, if you cannot use modular resources because you need to host `viam-server` on a non-Linux system or have a compilation issue, you can code a custom resource implementation, host it on a server, and add it as a [remote part](/hardware/multi-machine/add-a-remote-part/) of your machine.
+
+Once configured, you control the custom component with the Viam SDKs like any other component.
+
+### Steps
+
+{{< tabs >}}
+{{% tab name="Go" %}}
+
+1. Code a new model of a built-in resource type by creating a new interface that implements the required methods from its [API definition](/reference/apis/).
+2. Register the custom component on a new gRPC server instance and start the server.
+3. Add the server as a [remote part](/hardware/multi-machine/add-a-remote-part/) of your machine.
+4. (Optional) Ensure the remote server automatically starts when the machine boots.
+
+Each remote server can host one or many custom components.
+
+{{% /tab %}}
+{{% tab name="Python" %}}
+
+{{< alert title="Tip" color="tip" >}}
+For detailed instructions, see the full example in the [Python SDK documentation](https://python.viam.dev/examples/example.html#subclass-a-component).
+{{< /alert >}}
+
+1. Code a new model of a built-in resource type by subclassing it (for example, `sensor` or `arm`). Implement any required methods from its [API definition](/reference/apis/).
+2. Register the custom component on a new gRPC server instance using the [`viam.rpc` library](https://python.viam.dev/autoapi/viam/rpc/index.html).
+3. Add the server as a [remote part](/hardware/multi-machine/add-a-remote-part/) of your machine.
+4. (Optional) Ensure the remote server automatically starts when the machine boots.
+
+Each remote server can host one or many custom components.
+
+{{% /tab %}}
+{{< /tabs >}}
+
+{{% alert title="Important" color="note" %}}
+
+You must define all methods belonging to a built-in resource type when defining a new model. Otherwise, the class will not instantiate.
+
+- In Python, raise `NotImplementedError()` or use `pass` in methods you do not want to implement.
+- In Go, return `errUnimplemented`.
+
+{{% /alert %}}
+
+## Deploy a module using Docker
+
+In rare cases, you may need to package and deploy a module using Docker. Use cases include:
+
+- Your module has complex system dependencies that cannot be easily installed on a machine.
+- You use a large container image and some layers are already cached on the machine.
+- You have specific security requirements.
+
+If you deploy using Docker, create a "first run" script to handle setup. This is not recommended for modules that do not use Docker.
+
+### Use a `first_run` script
+
+1. Create a tarball containing:
+
+ - The module's entrypoint script or binary:
+
+ ```sh {class="command-line" data-prompt="$"}
+ #!/bin/bash
+ exec docker run
+ ```
+
+ - A [meta.json](/build-modules/module-reference/#metajson-schema) file.
+
+ - A first-run script that runs during setup:
+
+ ```sh {class="command-line" data-prompt="$"}
+ #!/usr/bin/env bash
+ docker pull mongo:6
+ echo "Setup complete."
+ exit 0
+ ```
+
+ [This example Makefile](https://github.com/viam-labs/wifi-sensor/blob/7823b6ad3edcbbbf20b06c34b3181453f5f3f078/Makefile) builds a module binary and bundles it with a meta.json and first-run script.
+
+2. Edit meta.json to include the `first_run` field:
+
+ ```json
+ {
+ "first_run": "first_run.sh"
+ }
+ ```
+
+3. Configure the module on your machine normally. The first-run script executes once when `viam-server` receives a new configuration, and once per version update.
+
+4. (Optional) To force the first-run script to run again without changing the module version, delete the marker file at `.viam/packages/data/module//bin.first_run_succeeded`.
+
+5. (Optional) The default first-run timeout is 1 hour. Adjust with `"first_run_timeout": "5m"` in the module configuration.
diff --git a/docs/build-modules/deploy-a-module.md b/docs/build-modules/deploy-a-module.md
index 0aa5c7c3b3..31b8503020 100644
--- a/docs/build-modules/deploy-a-module.md
+++ b/docs/build-modules/deploy-a-module.md
@@ -7,6 +7,7 @@ type: "docs"
description: "Package, upload, and distribute a module through the Viam registry."
date: "2025-01-30"
aliases:
+ - /operate/modules/deploy-a-module/
- /build/development/deploy-a-module/
- /development/deploy-a-module/
- /extend/modular-resources/upload/
diff --git a/docs/build-modules/manage-modules.md b/docs/build-modules/manage-modules.md
index d13e9d127a..5f3efbdf00 100644
--- a/docs/build-modules/manage-modules.md
+++ b/docs/build-modules/manage-modules.md
@@ -8,6 +8,7 @@ icon: true
tags: ["modular resources", "components", "services", "registry"]
description: "Update or delete your existing modules, or change their privacy settings."
aliases:
+ - /operate/modules/advanced/manage-modules/
- /use-cases/deploy-code/
- /use-cases/manage-modules/
- /how-tos/manage-modules/
diff --git a/docs/build-modules/module-reference.md b/docs/build-modules/module-reference.md
index 89d6867f57..31bd9bc895 100644
--- a/docs/build-modules/module-reference.md
+++ b/docs/build-modules/module-reference.md
@@ -7,6 +7,9 @@ type: "docs"
description: "Reference for module developers: lifecycle, interfaces, meta.json schema, CLI commands, environment variables, and registry rules."
date: "2025-03-05"
aliases:
+ - /operate/modules/advanced/logging/
+ - /operate/modules/advanced/metajson/
+ - /operate/modules/advanced/module-configuration/
- /development/module-reference/
- /operate/modules/lifecycle-module/
- /operate/modules/lifecycle-of-a-module/
@@ -17,6 +20,14 @@ This page is a reference for module developers. For step-by-step
instructions, see [Write a Module](/build-modules/write-a-driver-module/) and
[Deploy a Module](/build-modules/deploy-a-module/).
+## Module configuration {#module-configuration}
+
+For details on configuring modules and modular resources on a machine:
+
+- [Environment variables](#environment-variables) — custom env vars passed to your module
+- [Registry validation rules](#registry-validation-rules) — valid API and model identifier formats
+- [meta.json schema](#metajson-schema) — module metadata including visibility settings
+
## Module lifecycle
Every module, local or registry, runs as a separate child process alongside
diff --git a/docs/build-modules/overview.md b/docs/build-modules/overview.md
index ca9b4c71fe..bda49d4b44 100644
--- a/docs/build-modules/overview.md
+++ b/docs/build-modules/overview.md
@@ -6,6 +6,8 @@ layout: "docs"
type: "docs"
description: "Understand the two kinds of modules and how to extend your machine with custom hardware drivers and application logic."
aliases:
+ - /operate/modules/
+ - /operate/modules/advanced/
- /build-modules/from-hardware-to-logic/
---
diff --git a/docs/build-modules/platform-apis.md b/docs/build-modules/platform-apis.md
index 76a04e5643..a26f723f34 100644
--- a/docs/build-modules/platform-apis.md
+++ b/docs/build-modules/platform-apis.md
@@ -6,6 +6,7 @@ layout: "docs"
type: "docs"
description: "Write your validate and reconfigure functions to handle dependencies in your custom modular resource."
aliases:
+ - /operate/modules/advanced/platform-apis/
date: "2025-11-05"
---
diff --git a/docs/build-modules/use-registry-modules.md b/docs/build-modules/use-registry-modules.md
index 5555a8bb84..fe5399db85 100644
--- a/docs/build-modules/use-registry-modules.md
+++ b/docs/build-modules/use-registry-modules.md
@@ -6,6 +6,8 @@ layout: "docs"
type: "docs"
description: "Before writing a module, check whether one already exists. The registry has more than hardware drivers."
date: "2026-04-17"
+aliases:
+ - /registry/
---
Before you write a module, check whether one already exists. The Viam registry contains more than hardware drivers. Many software capabilities you might want to build are already published as modules you can configure and use directly.
diff --git a/docs/build-modules/write-a-cpp-module.md b/docs/build-modules/write-a-cpp-module.md
index 4dfe684bb5..0b310ba439 100644
--- a/docs/build-modules/write-a-cpp-module.md
+++ b/docs/build-modules/write-a-cpp-module.md
@@ -61,7 +61,7 @@ For example, the [sensor API](/reference/apis/components/sensor/) has a `GetRead
If instead of just getting readings, you actually have an encoder and need to be able to reset the zero position, use the [encoder API](/reference/apis/components/encoder/) so you can define functionality behind the `GetPosition` and `ResetPosition` methods.
In addition to the list of methods, another reason to choose one API over another is how certain APIs fit into the Viam ecosystem.
-For example, though you could technically implement a GPS as a sensor with just the `GetReadings` method, if you implement it as a movement sensor then you have access to methods like `GetCompassHeading` which allow you to use your GPS module with the [navigation service](/operate/reference/services/navigation/).
+For example, though you could technically implement a GPS as a sensor with just the `GetReadings` method, if you implement it as a movement sensor then you have access to methods like `GetCompassHeading` which allow you to use your GPS module with the [navigation service](/reference/services/navigation/).
For this reason, it's generally best to choose the API that most closely matches your hardware or software.
{{< /expand >}}
diff --git a/docs/build-modules/write-a-driver-module.md b/docs/build-modules/write-a-driver-module.md
index 91d8004460..7eaf692b56 100644
--- a/docs/build-modules/write-a-driver-module.md
+++ b/docs/build-modules/write-a-driver-module.md
@@ -7,6 +7,7 @@ type: "docs"
description: "Build a module that implements a resource API and runs as a separate process."
date: "2025-01-30"
aliases:
+ - /operate/modules/write-a-driver-module/
- /build/development/write-a-module/
- /development/write-a-module/
- /development/write-a-driver-module/
diff --git a/docs/hardware/common-components/add-a-base.md b/docs/hardware/common-components/add-a-base.md
index 084e84c690..3793edb89b 100644
--- a/docs/hardware/common-components/add-a-base.md
+++ b/docs/hardware/common-components/add-a-base.md
@@ -7,6 +7,8 @@ type: "docs"
description: "Add and configure a base to drive a mobile robot with movement commands."
date: "2025-03-07"
aliases:
+ - /operate/reference/components/base/
+ - /operate/reference/components/base/boat/
- /hardware-components/add-a-base/
---
diff --git a/docs/hardware/common-components/add-a-board.md b/docs/hardware/common-components/add-a-board.md
index f676b2dd4c..33c48f3c2f 100644
--- a/docs/hardware/common-components/add-a-board.md
+++ b/docs/hardware/common-components/add-a-board.md
@@ -7,6 +7,7 @@ type: "docs"
description: "Add and configure a board component to expose GPIO pins, analog readers, and digital interrupts."
date: "2025-03-07"
aliases:
+ - /operate/reference/components/board/
- /hardware-components/add-a-board/
---
diff --git a/docs/hardware/common-components/add-a-button.md b/docs/hardware/common-components/add-a-button.md
index 73f2930202..41996c815e 100644
--- a/docs/hardware/common-components/add-a-button.md
+++ b/docs/hardware/common-components/add-a-button.md
@@ -7,6 +7,7 @@ type: "docs"
description: "Add and configure a button component to detect presses from a physical button."
date: "2025-03-07"
aliases:
+ - /operate/reference/components/button/
- /hardware-components/add-a-button/
---
diff --git a/docs/hardware/common-components/add-a-camera.md b/docs/hardware/common-components/add-a-camera.md
index cb3d39bb79..a61af3a261 100644
--- a/docs/hardware/common-components/add-a-camera.md
+++ b/docs/hardware/common-components/add-a-camera.md
@@ -7,6 +7,7 @@ type: "docs"
description: "Add and configure a camera component, verify the feed, and capture an image programmatically."
date: "2025-01-30"
aliases:
+ - /operate/reference/components/camera/
- /build/foundation/add-a-camera/
- /foundation/add-a-camera/
- /hardware-components/add-a-camera/
@@ -332,6 +333,10 @@ Camera streams use a significant amount of CPU resources. The more CPU resources
{{< /expand >}}
+## Camera calibration
+
+If you are using a camera with the motion service for arm movement or pick-and-place, you may need to calibrate it. Calibration computes the camera's intrinsic and distortion parameters so the motion service can project 2D image coordinates into 3D workspace coordinates accurately. See [Calibrate a camera for motion planning](/motion-planning/frame-system/camera-calibration/).
+
## Related
- [Camera API reference](/reference/apis/components/camera/): full method documentation.
diff --git a/docs/hardware/common-components/add-a-gantry.md b/docs/hardware/common-components/add-a-gantry.md
index deafcaa0ab..4b757960ae 100644
--- a/docs/hardware/common-components/add-a-gantry.md
+++ b/docs/hardware/common-components/add-a-gantry.md
@@ -7,6 +7,7 @@ type: "docs"
description: "Add and configure a gantry component for precise linear positioning along one or more axes."
date: "2025-03-07"
aliases:
+ - /operate/reference/components/gantry/
- /hardware-components/add-a-gantry/
---
diff --git a/docs/hardware/common-components/add-a-generic.md b/docs/hardware/common-components/add-a-generic.md
index 4273ecf4c2..ad2c95bc6b 100644
--- a/docs/hardware/common-components/add-a-generic.md
+++ b/docs/hardware/common-components/add-a-generic.md
@@ -7,6 +7,7 @@ type: "docs"
description: "Add and configure a generic component for hardware that doesn't fit any other component type."
date: "2025-03-07"
aliases:
+ - /operate/reference/components/generic/
- /hardware-components/add-a-generic/
---
diff --git a/docs/hardware/common-components/add-a-gripper.md b/docs/hardware/common-components/add-a-gripper.md
index a212ac17b7..21144699fb 100644
--- a/docs/hardware/common-components/add-a-gripper.md
+++ b/docs/hardware/common-components/add-a-gripper.md
@@ -7,6 +7,7 @@ type: "docs"
description: "Add and configure a gripper component to open, close, and grasp objects."
date: "2025-03-07"
aliases:
+ - /operate/reference/components/gripper/
- /hardware-components/add-a-gripper/
---
diff --git a/docs/hardware/common-components/add-a-motor.md b/docs/hardware/common-components/add-a-motor.md
index 4e1c736a50..1ee617340f 100644
--- a/docs/hardware/common-components/add-a-motor.md
+++ b/docs/hardware/common-components/add-a-motor.md
@@ -7,6 +7,7 @@ type: "docs"
description: "Add and configure a motor component and test it from the Viam app."
date: "2025-03-07"
aliases:
+ - /operate/reference/components/motor/
- /hardware-components/add-a-motor/
---
diff --git a/docs/hardware/common-components/add-a-movement-sensor.md b/docs/hardware/common-components/add-a-movement-sensor.md
index 3d0867f3d0..5a930caee0 100644
--- a/docs/hardware/common-components/add-a-movement-sensor.md
+++ b/docs/hardware/common-components/add-a-movement-sensor.md
@@ -7,6 +7,8 @@ type: "docs"
description: "Add and configure a movement sensor like a GPS, IMU, or odometry source."
date: "2025-03-07"
aliases:
+ - /operate/reference/components/movement-sensor/
+ - /operate/reference/components/movement-sensor/set-up-base-station/
- /hardware-components/add-a-movement-sensor/
---
diff --git a/docs/hardware/common-components/add-a-power-sensor.md b/docs/hardware/common-components/add-a-power-sensor.md
index df1219b740..ae1e090933 100644
--- a/docs/hardware/common-components/add-a-power-sensor.md
+++ b/docs/hardware/common-components/add-a-power-sensor.md
@@ -7,6 +7,7 @@ type: "docs"
description: "Add and configure a power sensor to monitor voltage, current, and power consumption."
date: "2025-03-07"
aliases:
+ - /operate/reference/components/power-sensor/
- /hardware-components/add-a-power-sensor/
---
diff --git a/docs/hardware/common-components/add-a-sensor.md b/docs/hardware/common-components/add-a-sensor.md
index 75c64af9f2..fcc5c1983f 100644
--- a/docs/hardware/common-components/add-a-sensor.md
+++ b/docs/hardware/common-components/add-a-sensor.md
@@ -7,6 +7,7 @@ type: "docs"
description: "Add and configure a sensor to read environmental data like temperature, humidity, or distance."
date: "2025-03-07"
aliases:
+ - /operate/reference/components/sensor/
- /hardware-components/add-a-sensor/
---
diff --git a/docs/hardware/common-components/add-a-switch.md b/docs/hardware/common-components/add-a-switch.md
index 04ecddb97d..86578dbb5b 100644
--- a/docs/hardware/common-components/add-a-switch.md
+++ b/docs/hardware/common-components/add-a-switch.md
@@ -7,6 +7,7 @@ type: "docs"
description: "Add and configure a switch component to read and set the position of a multi-position switch."
date: "2025-03-07"
aliases:
+ - /operate/reference/components/switch/
- /hardware-components/add-a-switch/
---
diff --git a/docs/hardware/common-components/add-an-arm.md b/docs/hardware/common-components/add-an-arm.md
index 2ece18425e..a3a29ccc1c 100644
--- a/docs/hardware/common-components/add-an-arm.md
+++ b/docs/hardware/common-components/add-an-arm.md
@@ -7,6 +7,9 @@ type: "docs"
description: "Add and configure a robotic arm, verify joint motion, and test end-effector positioning."
date: "2025-03-07"
aliases:
+ - /operate/reference/components/arm/
+ - /operate/reference/components/arm/eva/
+ - /operate/reference/components/arm/yahboom-dofbot/
- /hardware-components/add-an-arm/
---
diff --git a/docs/hardware/common-components/add-an-encoder.md b/docs/hardware/common-components/add-an-encoder.md
index d7e37a1e9f..d357cde70a 100644
--- a/docs/hardware/common-components/add-an-encoder.md
+++ b/docs/hardware/common-components/add-an-encoder.md
@@ -7,6 +7,7 @@ type: "docs"
description: "Add and configure an encoder to track motor position and direction."
date: "2025-03-07"
aliases:
+ - /operate/reference/components/encoder/
- /hardware-components/add-an-encoder/
---
diff --git a/docs/hardware/common-components/add-an-input-controller.md b/docs/hardware/common-components/add-an-input-controller.md
index ddc874763f..fa70452901 100644
--- a/docs/hardware/common-components/add-an-input-controller.md
+++ b/docs/hardware/common-components/add-an-input-controller.md
@@ -7,6 +7,7 @@ type: "docs"
description: "Add and configure a gamepad, joystick, or other input device for manual machine control."
date: "2025-03-07"
aliases:
+ - /operate/reference/components/input-controller/
- /hardware-components/add-an-input-controller/
---
diff --git a/docs/hardware/configure-hardware.md b/docs/hardware/configure-hardware.md
index e3c8bf8742..bc03007dcc 100644
--- a/docs/hardware/configure-hardware.md
+++ b/docs/hardware/configure-hardware.md
@@ -7,6 +7,17 @@ type: "docs"
description: "Understand how Viam represents hardware, add components to your machine, and configure them."
date: "2025-03-07"
aliases:
+ - /program/extend/modular-resources/configure/
+ - /extend/modular-resources/configure/
+ - /modular-resources/configure/
+ - /registry/configure/
+ - /registry/modular-resources/
+ - /configure/
+ - /manage/configuration/
+ - /build/configure/
+ - /registry/program/extend/modular-resources/configure/
+ - /how-tos/use-cases/configure/
+ - /use-cases/configure/
- /hardware-components/add-a-component/
- /hardware/add-a-component/
- /operate/modules/configure-modules/
@@ -58,7 +69,7 @@ If no model exists for your hardware, you can [write your own module](/build-mod
-Browse all available components and services in the [Viam registry](https://app.viam.com/registry).
+Browse all available components and services in the [Viam registry](https://app.viam.com/registry). The registry also contains software modules that add capabilities beyond hardware drivers: vision and ML services, conversational and audio processing, device discovery, custom logic, and external integrations.
## Switching hardware without changing code
diff --git a/docs/hardware/multi-machine/overview.md b/docs/hardware/multi-machine/overview.md
index 6a98116615..88cf7df628 100644
--- a/docs/hardware/multi-machine/overview.md
+++ b/docs/hardware/multi-machine/overview.md
@@ -7,6 +7,13 @@ type: "docs"
description: "Connect multiple computers so one machine can access another's components and services."
date: "2026-04-16"
aliases:
+ - /manage/parts-and-remotes/
+ - /build/configure/parts-and-remotes/
+ - /configure/parts/
+ - /build/configure/parts/
+ - /architecture/parts/
+ - /operate/reference/architecture/
+ - /operate/reference/architecture/machine-to-machine-comms/
- /operate/reference/architecture/parts/
---
diff --git a/docs/motion-planning/frame-system/camera-calibration.md b/docs/motion-planning/frame-system/camera-calibration.md
index a9e8da0122..23e741ab1a 100644
--- a/docs/motion-planning/frame-system/camera-calibration.md
+++ b/docs/motion-planning/frame-system/camera-calibration.md
@@ -9,6 +9,7 @@ aliases:
- /work-cell-layout/calibrate-camera-to-robot/
- /build/work-cell-layout/calibrate-camera-to-robot/
- /motion-planning/camera-calibration/
+ - /operate/reference/components/camera/calibrate/
---
Configuring a camera's frame tells the motion service where the camera
diff --git a/docs/motion-planning/frame-system/overview.md b/docs/motion-planning/frame-system/overview.md
index 821cc5d8cc..913f09a2aa 100644
--- a/docs/motion-planning/frame-system/overview.md
+++ b/docs/motion-planning/frame-system/overview.md
@@ -6,6 +6,7 @@ layout: "docs"
type: "docs"
description: "Build a unified coordinate tree so all components agree on where things are in physical space."
aliases:
+ - /operate/reference/kinematic-chain-config/
- /work-cell-layout/define-your-frame-system/
- /build/work-cell-layout/define-your-frame-system/
- /operate/mobility/move-arm/frame-how-to/
diff --git a/docs/operate/_index.md b/docs/operate/_index.md
deleted file mode 100644
index 358d33009e..0000000000
--- a/docs/operate/_index.md
+++ /dev/null
@@ -1,67 +0,0 @@
----
-linkTitle: "Build and Integrate"
-title: "Build and Integrate"
-weight: 150
-layout: "docs"
-toc_hide: true
-type: "docs"
-no_list: true
-noedit: true
-open_on_desktop: true
-overview: true
-description: "Install Viam on any device and integrate your hardware. Then you can control your device and attached physical hardware securely from anywhere in the world."
-notoc: true
-aliases:
- - /build/
- - /operate/mobility/use-input-to-act/
----
-
-To get started, install Viam on any device and integrate your hardware. Then you can control your device and any attached physical hardware securely from anywhere in the world.
-
-
-
diff --git a/docs/operate/control/_index.md b/docs/operate/control/_index.md
deleted file mode 100644
index 58f211c015..0000000000
--- a/docs/operate/control/_index.md
+++ /dev/null
@@ -1,10 +0,0 @@
----
-linkTitle: "Build apps"
-weight: 200
-layout: "empty"
-type: "docs"
-empty_node: true
-open_on_desktop: true
-header_only: true
-canonical: "/operate/control/web-app/"
----
diff --git a/docs/operate/control/api-keys.md b/docs/operate/control/api-keys.md
deleted file mode 100644
index 8e6a708765..0000000000
--- a/docs/operate/control/api-keys.md
+++ /dev/null
@@ -1,54 +0,0 @@
----
-title: "Viam API keys"
-linkTitle: "API keys"
-weight: 50
-layout: "docs"
-type: "docs"
-description: "Run control logic on a machine."
-images: ["/general/code.png"]
-date: "2025-01-15"
----
-
-API keys grant access to organizations, locations, and machines.
-
-To view all API keys in use for your organization and the locations and machines inside it, click on the organization dropdown in the top navigation bar and click on **Settings**.
-
-{{}}
-
-## Add an API key
-
-On your organization's page, click **Generate key** to generate a new key.
-Optionally, give the key a name.
-Click on the **Entity** menu and choose what organization, location, or machine you want the key to grant access to.
-If you select organization, your key grants access to all locations and machines contained within that organization.
-If you select location, your key grants access to all of the machines contained within that location.
-
-For **Role**, assign either an **Owner** or **Operator** role.
-See [Permissions](/manage/manage/rbac/) for information about the privilege each role entails at each resource level.
-
-## Change an API key's access
-
-To edit an API key, click on **Show details** in the key's row of the key table's **Entities** column.
-
-{{}}
-
-To edit the role, click on the dropdown menu next to the role and select **Owner** or **Operator**.
-See [Permissions](/manage/manage/rbac/) for information about the privilege each role entails at each resource level.
-
-To change the entities it is able to access, click **+ Grant additional access**.
-Select which organization, location, or machine you want the key to grant access to.
-Click **Choose** to confirm your selection.
-
-## Rotate an API key
-
-Viam supports flexible key rotation with up to two keys in use at one time.
-If you ever need to rotate an API key you can use the web UI:
-
-1. On [Viam](https://app.viam.com/), click on the organization dropdown in the top navigation bar.
-1. Click on **Settings and invites**.
-1. Click the **Generate Key** button on the organization setting page to generate a new key.
-1. Update all code and integrations that use the key.
-1. Find the old API key on the page and click on **Show details**.
-1. Then click **Remove API key**.
-
-Alternatively, you can use the [`RotateKey`](/reference/apis/fleet/#rotatekey) API method.
diff --git a/docs/operate/control/headless-app.md b/docs/operate/control/headless-app.md
deleted file mode 100644
index 27d3bc5361..0000000000
--- a/docs/operate/control/headless-app.md
+++ /dev/null
@@ -1,411 +0,0 @@
----
-linkTitle: "Create a headless app"
-title: "Create a headless app"
-weight: 30
-layout: "docs"
-type: "docs"
-description: "Run control logic on a machine."
-images: ["/general/code.png"]
-aliases:
- - /product-overviews/sdk-as-client/
- - /program/sdk-as-client/
- - /program/sdks/
- - /program/
- - /program/run/
- - /program/debug/
- - /how-tos/develop-app/
- - /use-cases/develop-app/
- - /product-overviews/sdk-as-client/
----
-
-To write control logic for your machine that will run without a user interface, you can use the Python, Go, or C++ SDK.
-The SDKs each include similar methods to hit Viam's [gRPC API](https://github.com/viamrobotics/api) endpoints.
-
-## Decide where to run your code
-
-You can run your code directly on the [machine's](/operate/install/setup/) single-board computer (SBC), or you can run it from a separate computer connected to the internet or to the same local network as your machine's SBC or microcontroller.
-
-### On a separate computer
-
-We recommend running your code on a laptop, desktop, or server if:
-
-- You are using computationally-intensive programs involving, for example, computer vision or motion planning, and
-- You have a stable internet connection
-
-The client code will establish a connection to the instance of `viam-server` on your machine's SBC over [LAN or WAN](/reference/sdks/connectivity/).
-
-### On the machine itself
-
-We recommend running your code on the SBC that directly controls your hardware if:
-
-- Your machines have intermittent or no network connectivity, or
-- You want to reduce latency, for example for running [PID control loops](https://en.wikipedia.org/wiki/Proportional%E2%80%93integral%E2%80%93derivative_controller), or
-- Your machine runs continuously (for example, an air quality sensor) and it is impractical to constantly run a client from your development computer
-
-## Install an SDK
-
-Install your preferred Viam SDK on the computer or SBC where you plan to run the control script.
-
-{{< tabs >}}
-{{% tab name="Python" %}}
-
-If you are using the Python SDK, [set up a virtual environment](/reference/sdks/python/python-venv/) to package the SDK inside before running your code, avoiding conflicts with other projects or your system.
-
-For macOS (both Intel `x86_64` and Apple Silicon) or Linux (`x86`, `aarch64`, `armv6l`), run the following commands:
-
-```sh {class="command-line" data-prompt="$"}
-python3 -m venv .venv
-source .venv/bin/activate
-pip install viam-sdk
-```
-
-Windows is not supported.
-If you are using Windows, use the [Windows Subsystem for Linux (WSL)](https://learn.microsoft.com/en-us/windows/wsl/install) and install the Python SDK using the preceding instructions for Linux.
-For other unsupported systems, see [Installing from source](https://python.viam.dev/#installing-from-source).
-
-If you intend to use the [ML (machine learning) model service](/vision/configure/), use the following command instead, which installs additional required dependencies along with the Python SDK:
-
-```sh {class="command-line" data-prompt="$"}
-pip install 'viam-sdk[mlmodel]'
-```
-
-{{% /tab %}}
-{{% tab name="Go" %}}
-
-Run the following command to install the [Viam Go SDK](https://pkg.go.dev/go.viam.com/rdk):
-
-```sh {class="command-line" data-prompt="$"}
-go get go.viam.com/rdk/robot/client
-```
-
-{{% /tab %}}
-{{% tab name="C++" %}}
-
-Follow the [instructions on the GitHub repository](https://github.com/viamrobotics/viam-cpp-sdk/blob/main/BUILDING.md).
-
-{{% /tab %}}
-{{< /tabs >}}
-
-## Authenticate
-
-You can find sample connection code on each [machine's](/operate/install/setup/) **CONNECT** tab.
-Select your preferred **Language** to display a code snippet, with connection code as well as some calls to the APIs of the resources you've configured on your machine.
-
-You can use the toggle to include the machine API key and API key ID, though we strongly recommend storing your API keys in environment variables to reduce the risk of accidentally sharing your API key and granting access to your machines.
-
-If your code will connect to multiple machines or use [Platform APIs](/reference/apis/#platform-apis) you can create an API key with broader access.
-
-## Write your control code
-
-For API reference including code snippets for each method, see [Viam's Client APIs](/reference/apis/).
-
-{{< expand "Example code for moving a rover in a square" >}}
-
-The following code moves a mobile robot base in a square using the [base API](/reference/apis/components/base/).
-
-{{< tabs >}}
-{{% tab name="Python" %}}
-
-```python {class="line-numbers linkable-line-numbers"}
-import asyncio
-
-from viam.components.base import Base
-from viam.robot.client import RobotClient
-from viam.rpc.dial import Credentials, DialOptions
-
-
-async def connect():
- opts = RobotClient.Options.with_api_key(
- # TODO: Replace "" (including brackets) with your
- # machine's API key
- api_key='',
- # TODO: Replace "" (including brackets) with your machine's
- # API key ID
- api_key_id=''
- )
- # TODO: Replace "" with address from the CONNECT tab.
- return await RobotClient.at_address('', opts)
-
-
-async def moveInSquare(base):
- for _ in range(4):
- # moves the rover forward 500mm at 500mm/s
- await base.move_straight(velocity=500, distance=500)
- print("move straight")
- # spins the rover 90 degrees at 100 degrees per second
- await base.spin(velocity=100, angle=90)
- print("spin 90 degrees")
-
-
-async def main():
- async with connect() as machine:
- roverBase = Base.from_robot(machine, 'viam_base')
-
- # Move the rover in a square
- await moveInSquare(roverBase)
-
-
-if __name__ == '__main__':
- asyncio.run(main())
-```
-
-{{% /tab %}}
-{{% tab name="Go" %}}
-
-```go {class="line-numbers linkable-line-numbers"}
-package main
-
-import (
- "context"
-
- "go.viam.com/rdk/components/base"
- "go.viam.com/rdk/logging"
- "go.viam.com/rdk/robot/client"
- "go.viam.com/rdk/utils")
-
-func moveInSquare(ctx context.Context, base base.Base, logger logging.Logger) {
- for i := 0; i < 4; i++ {
- // moves the rover forward 600mm at 500mm/s
- base.MoveStraight(ctx, 600, 500.0, nil)
- logger.Info("move straight")
- // spins the rover 90 degrees at 100 degrees per second
- base.Spin(ctx, 90, 100.0, nil)
- logger.Info("spin 90 degrees")
- }
-}
-
-func main() {
- logger := logging.NewLogger("client")
- machine, err := client.New(
- context.Background(),
- // TODO: Replace "" with address from the CONNECT tab.
- "",
- logger,
- client.WithDialOptions(utils.WithEntityCredentials(
- // TODO: Replace "" (including brackets) with your machine's
- // API key ID
- "",
- utils.Credentials{
- Type: utils.CredentialsTypeAPIKey,
- // TODO: Replace "" (including brackets) with your machine's
- // API key
- Payload: "",
- })),
- )
- if err != nil {
- logger.Fatal(err)
- }
- defer machine.Close(context.Background())
-
- // Get the base from the rover
- roverBase, err := base.FromProvider(machine, "viam_base")
- if err != nil {
- logger.Fatalf("cannot get base: %v", err)
- }
-
- // Move the rover in a square
- moveInSquare(context.Background(), roverBase, logger)
-}
-```
-
-{{% /tab %}}
-{{% tab name="C++" %}}
-
-```cpp {class="line-numbers linkable-line-numbers"}
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-using namespace viam::sdk;
-using std::cerr;
-using std::cout;
-using std::endl;
-
-void move_in_square(std::shared_ptr base) {
- for (int i = 0; i < 4; ++i) {
- cout << "Move straight" << endl;
- // Move the base forward 600mm at 500mm/s
- base->move_straight(500, 500);
- cout << "Spin" << endl;
- // Spin the base by 90 degree at 100 degrees per second
- base->spin(90, 100);
- }
-}
-
-int main() {
- // Every Viam C++ SDK program must have one and only one Instance object which is created
- // before
- // any other C++ SDK objects and stays alive until all Viam C++ SDK objects are destroyed.
- Instance inst;
-
- // TODO: Replace "" with address from the CONNECT tab.
- std::string host("");
- DialOptions dial_opts;
- // TODO: Replace "" with your machine's API key ID
- dial_opts.set_entity(std::string(""));
- // TODO: Replace "" with your machine's API key
- Credentials credentials("api-key", "");
- dial_opts.set_credentials(credentials);
- boost::optional opts(dial_opts);
- Options options(0, opts);
-
- auto machine = RobotClient::at_address(host, options);
-
- std::cout << "Resources:\n";
- for (const Name& resource : machine->resource_names()) {
- std::cout << "\t" << resource << "\n";
- }
-
- std::string base_name("viam_base");
-
- cout << "Getting base: " << base_name << endl;
- std::shared_ptr base;
- try {
- base = machine->resource_by_name(base_name);
-
- move_in_square(base);
-
- } catch (const std::exception& e) {
- cerr << "Failed to find " << base_name << ". Exiting." << endl;
- throw;
- }
- return EXIT_SUCCESS;
-}
-```
-
-{{% /tab %}}
-{{< /tabs >}}
-
-{{< /expand >}}
-
-{{< expand "Example Python code for turning on a fan based on sensor readings" >}}
-
-The following example from [Automate air filtration with air quality sensors](https://codelabs.viam.com/guide/air-quality/index.html?index=..%2F..index#0) uses both the sensor API's [GetReadings](/reference/apis/components/sensor/#getreadings) command as well as custom functionality with the generic [DoCommand](/reference/apis/components/generic/#docommand).
-
-```python {class="line-numbers linkable-line-numbers"}
-import asyncio
-import os
-from dotenv import load_dotenv
-from viam.logging import getLogger
-from viam.robot.client import RobotClient
-from viam.components.sensor import Sensor
-from viam.components.generic import Generic
-
-load_dotenv()
-LOGGER = getLogger(__name__)
-
-robot_api_key = os.getenv('MACHINE_API_KEY') or ''
-robot_api_key_id = os.getenv('MACHINE_API_KEY_ID') or ''
-robot_address = os.getenv('MACHINE_ADDRESS') or ''
-
-# Define the sensor and plug names on the CONFIGURE tab
-sensor_name = os.getenv("SENSOR_NAME", "")
-plug_name = os.getenv("PLUG_NAME", "")
-
-
-async def connect():
- opts = RobotClient.Options.with_api_key(
- api_key=robot_api_key,
- api_key_id=robot_api_key_id
- )
- return await RobotClient.at_address(robot_address, opts)
-
-
-async def main():
- async with connect() as machine:
- pms_7003 = Sensor.from_robot(machine, sensor_name)
- kasa_plug = Generic.from_robot(machine, plug_name)
-
- # Define unhealthy thresholds
- unhealthy_thresholds = {
- 'pm2_5_atm': 35.4,
- 'pm10_atm': 150
- }
-
- while True:
- readings = await pms_7003.get_readings()
- # Check if any of the PM values exceed the unhealthy thresholds
- if any(readings.get(pm_type, 0) > threshold for pm_type,
- threshold in unhealthy_thresholds.items()):
- LOGGER.info('UNHEALTHY.')
- await kasa_plug.do_command({"toggle_on": []})
- else:
- LOGGER.info('HEALTHY!')
- await kasa_plug.do_command({"toggle_off": []})
-
- # wait before checking again
- await asyncio.sleep(10)
-
-if __name__ == '__main__':
- asyncio.run(main())
-```
-
-{{< /expand >}}
-
-## Run your code
-
-You can run your code manually from your terminal, or you can automatically run it each time your machine starts.
-
-### Run your code manually
-
-To run your code on a laptop or desktop, or to run it on your machine's SBC manually for testing purposes, execute the following command in a terminal:
-
-{{< tabs >}}
-{{% tab name="Python" %}}
-
-```sh {id="terminal-prompt" class="command-line" data-prompt="$"}
-python3 /home/myName/project/my_cool_script.py
-```
-
-{{% /tab %}}
-{{% tab name="Go" %}}
-
-```sh {id="terminal-prompt" class="command-line" data-prompt="$"}
-go run /home/myName/project/my_cool_script.go
-```
-
-{{% /tab %}}
-{{% tab name="C++" %}}
-
-For information on running C++ code see [the instructions on GitHub](https://github.com/viamrobotics/viam-cpp-sdk/blob/main/BUILDING.md).
-
-{{% /tab %}}
-{{< /tabs >}}
-
-### Run your control code in a module
-
-To run your control code on your machine, create a {{< glossary_tooltip term_id="module" text="module" >}} and deploy it to your machine.
-You can add functionality to the `DoCommand()` method to, for example, start and stop the control code.
-
-For more information, see [Create a module with machine control logic](/operate/modules/write-a-logic-module/#5-implement-docommand).
-
-## Debug
-
-Read and filter a machine's logs to view updates from your machine's `viam-server` instance and troubleshoot issues with your program.
-
-{{< tabs >}}
-{{% tab name="App UI" %}}
-
-Navigate to the **LOGS** tab of your machine's page.
-
-Select from the **Levels** dropdown menu to filter the logs by severity level:
-
-
-
-{{% /tab %}}
-{{% tab name="Command line" %}}
-
-```sh {class="command-line" data-prompt="$"}
-viam machines part logs --part= --tail=true
-```
-
-{{% /tab %}}
-{{< /tabs >}}
diff --git a/docs/operate/control/kiosk-app.md b/docs/operate/control/kiosk-app.md
deleted file mode 100644
index e77f552493..0000000000
--- a/docs/operate/control/kiosk-app.md
+++ /dev/null
@@ -1,10 +0,0 @@
----
-linkTitle: "Create a kiosk app"
-title: "Create a kiosk app"
-weight: 40
-layout: "docs"
-type: "docs"
-no_list: true
-description: "TODO"
-draft: true
----
diff --git a/docs/operate/control/mobile-app.md b/docs/operate/control/mobile-app.md
deleted file mode 100644
index 2606ce0618..0000000000
--- a/docs/operate/control/mobile-app.md
+++ /dev/null
@@ -1,105 +0,0 @@
----
-linkTitle: "Create a mobile app"
-title: "Create a mobile app"
-weight: 20
-layout: "docs"
-type: "docs"
-description: "Create a custom user interface for interacting with machines from a mobile device."
----
-
-You can use Viam's [Flutter SDK](https://flutter.viam.dev/) to create a custom mobile application to interact with your devices.
-The Flutter SDK includes:
-
-- Implementation of the standard component and service APIs to control your hardware and software
-- Widgets to ease the development process
-- Authentication tools so users can log in securely
-
-## Install the Flutter SDK
-
-Run the following command in your terminal to install the Viam Flutter SDK:
-
-```sh {id="terminal-prompt" class="command-line" data-prompt="$"}
-flutter pub add viam_sdk
-```
-
-## Connect to your machine
-
-You can find sample connection code on each [machine's](/operate/install/setup/) **CONNECT** tab.
-Select **Flutter** to display a code snippet with connection code as well as some calls to the APIs of the resources you've configured on your machine.
-
-You can use the toggle to include the machine API key and API key ID, though we strongly recommend storing your API keys in environment variables to reduce the risk of accidentally sharing your API key and granting access to your machines.
-
-If your code will connect to multiple machines or use [Platform APIs](/reference/apis/#platform-apis) you can create an API key with broader access.
-
-## Write your app
-
-Refer to the [Viam Flutter SDK](https://flutter.viam.dev/) documentation for available methods and widgets.
-
-### Example usage
-
-The following code, part of [Drive a rover in a square in 2 minutes](/tutorials/control/drive-rover/), shows how you could move a robotic rover base in a square using the base API's [`moveStraight`](https://flutter.viam.dev/viam_sdk/Base/moveStraight.html) and [`spin`](https://flutter.viam.dev/viam_sdk/Base/spin.html) methods:
-
-```dart {class="line-numbers linkable-line-numbers"}
-import 'package:flutter/material.dart';
-import 'package:viam_sdk/viam_sdk.dart';
-import 'package:viam_sdk/widgets.dart';
-
-class BaseScreen extends StatelessWidget {
- final Base base;
-
- const BaseScreen(this.base, {super.key});
-
- Future moveSquare() async {
- for (var i=0; i<4; i++) {
- await base.moveStraight(500, 500);
- await base.spin(90, 100);
- }
- }
-
- @override
- Widget build(BuildContext context) {
- return Scaffold(
- appBar: AppBar(title: Text(base.name)),
- body: Center(
- child: Column(
- mainAxisAlignment: MainAxisAlignment.center,
- children: [
- ElevatedButton(
- onPressed: moveSquare,
- child: const Text('Move Base in Square'),
- ),
- ]))
- ,);}}
-```
-
-{{
|
-| `env` | object | Optional | Environment variables available to the module. For example `{ "API_KEY": "${environment.API_KEY}" }`. Some modules require that you set environment variables as part of configuration. For more information, see [environment variables](#environment-variables). You can add and edit `env` by switching from **Builder** to **{} JSON** mode in the **CONFIGURE** tab. |
-| `executable_path` | string | Local modules only | The path to the module's executable file. Only applicable to, and required for, local modules. Registry modules use the `entrypoint` in the [meta.json file](/operate/modules/advanced/metajson/) instead. |
-| `disabled` | boolean | Optional | Whether to disable the module. Default: `false`. |
-| `notes` | string | Optional | Descriptive text to document the purpose, configuration details, or other important information about this module. |
-| `log_level` | object | Optional | Set the log level for the module. See [Logging](/operate/reference/viam-server/#logging). |
-| `first_run_timeout` | number | Optional | The timeout duration for the first run script. Default: `60m`. |
-| `tcp_mode` | boolean | Optional | Whether to start the module with a TCP connection. Regardless of the value set here, if the environment variable `VIAM_TCP_SOCKETS` is set to true, `viam-server` will start the module with a TCP connection. TCP mode is currently only supported for Python and C++ modules. Default: `false`. |
-
-### Module versioning
-
-You can configure how each module on your machine updates itself when a newer version becomes available from the Viam Registry.
-By default, a newly-added module is set to pin to the latest release (**Latest**) of the version you added.
-
-To change the update strategy for your module, set the **Pinned version type** for your module in its module card on the **CONFIGURE** tab.
-
-{{}}
-
-The following update options are available:
-
-- **Patch (X.Y.Z)**: Do not update to any other version.
-- **Minor (X.Y.\*)**: Only update to newer patch releases of the same minor release branch.
- The module will automatically restart and update itself whenever new updates within the same minor release are available in the Viam Registry.
- For example, use this option to permit a module with version `1.2.3` to update to version `1.2.4` or `1.2.5` but not `1.3.0` or `2.0.0`.
-- **Major (X.\*)**: Only update to newer patch and minor releases of the same major release branch.
- The module will automatically restart and update itself whenever new updates within the same major release are available in the Viam Registry.
- For example, use this option to permit a module with version `1.2.3` to update to version `1.2.4` or `1.3.0` but not `2.0.0` or `3.0.0`.
-- **Latest (`latest`)**: Always update to the latest version of this module available from the Viam Registry as soon as a new version becomes available.
- This is the default.
-- **Latest with prerelease (`latest-with-prerelease`)**: Always update to the latest release or prerelease version of this module available from the Viam Registry as soon as the new version becomes available.
-
-When using the **Patch (X.Y.Z)** version type, you may select any patch version of the module from the **Version** dropdown menu, including past versions if desired.
-
-The current deployed version of your module and the latest version of that module available from the Viam Registry are shown on this module card for your reference.
-
-{{% alert title="Caution" color="caution" %}}
-For any version type other than **Patch (X.Y.Z)**, the module will upgrade as soon as an update that matches that specified version type is available, which will **restart the module**.
-If, for example, the module provides a motor component, and the motor is running, it will stop while the module upgrades.
-{{% /alert %}}
-
-If a module appears in both a {{< glossary_tooltip term_id="fragment" text="fragment" >}} and the part configuration, or in multiple fragments, Viam imports the newest of the configured versions.
-
-### Environment variables
-
-Each module has access to the following default environment variables.
-Not all of these variables are automatically available on [local modules](/operate/modules/write-a-driver-module/#3-test-locally); you can manually set variables your module requires if necessary.
-
-
-| Name | Description | Automatically set on local modules? |
-| ---- | ----------- | ----------------------------------- |
-| `VIAM_HOME` | The root of the `viam-server` configuration. Default for Linux, macOS, and WSL: $HOME/.viam Default for Windows: C:\WINDOWS\system32\config\systemprofile\\.viam |
|
-| `VIAM_MODULE_ROOT` | The root of the module install directory. The module process uses this directory as its current working directory (`cwd`). This variable is useful for file navigation that is relative to the root of the module. Example: /opt/my-module/verxxxx-my-module/ |
|
-| `VIAM_RESOURCE_CONFIGURATION_TIMEOUT` | Duration that resources are allowed to configure or reconfigure. Example: `4m0s` Default: `2m0s`. |
|
-| `VIAM_MODULE_STARTUP_TIMEOUT` | Duration that modules are allowed to start up. Example: `7m15s` Default: 5 minutes. |
|
-| `VIAM_MODULE_DATA` | A persistent folder location a module can use to store data across reboots and version updates. The folder will be removed when the module is removed from the machine, including when disabled in the machine configuration. This location is a good place to store [python virtual environments](/reference/sdks/python/python-venv/). Example: $VIAM_HOME/module-data/cloud-machine-id/my-module-name/ |
|
-| `VIAM_MODULE_ID` | The module ID of the module. Example: `viam:realsense` | |
-| `VIAM_API_KEY` | An API key with access to the machine where this instance of the module is running. |
|
-| `VIAM_API_KEY_ID` | The ID of the API key with access to the machine where this instance of the module is running. |
|
-| `VIAM_MACHINE_PART_ID` | The ID of the part where this instance of the module is running. | |
-| `VIAM_MACHINE_ID` | The ID of the machine where this instance of the module is running. | |
-| `VIAM_MACHINE_FQDN` | The {{< glossary_tooltip term_id="machine-fqdn" text="fully qualified domain name" >}} of the machine where this instance of the module is running. | |
-| `VIAM_LOCATION_ID` | The ID of the {{< glossary_tooltip term_id="location" text="location" >}} that owns the machine where this instance of the module is running. | |
-| `VIAM_PRIMARY_ORG_ID` | The ID of the {{< glossary_tooltip term_id="organization" text="organization" >}} that owns the machine where this instance of the module is running. | |
-
-#### Set additional environment variables
-
-You can configure additional environment variables for your module, using your choice of variable name and value.
-For example, you could create a variable `MODULE_USER` with a string value:
-
-```json {class="line-numbers linkable-line-numbers"}
-{
- "modules": [
- {
- ...
- "env": {
- "MODULE_USER": "my-username"
- }
- }
- ]
-}
-```
-
-To access any system environment variable that `viam-server` has access to, use the notation `${environment.}` where `` represents a system environment variable, like `PATH`, `USER`, or `PWD`.
-For example, you can use `${environment.HOME}` to access the `HOME` environment variable for the user running `viam-server`:
-
-```json {class="line-numbers linkable-line-numbers"}
-{
- "modules": [
- {
- ...
- "env": {
- "PATH": "/home/username/bin:${environment.PATH}"
- }
- }
- ]
-}
-```
-
-#### Access environment variables from your module
-
-You can access module environment variables from within your module code in the same way you would access any other environment variables.
-For example, you could use the following Python code to access the `VIAM_HOME` variable:
-
-```python
-import os
-
-viam_home = os.environ.get("VIAM_HOME")
-```
-
-## Configure an unlisted module
-
-To configure a module that is uploaded to the Viam Registry but has [visibility](/operate/modules/advanced/manage-modules/#change-module-visibility) set to **Unlisted** (`public_unlisted`), you need to manually add the module to your configuration:
-
-{{% hiddencontent %}}
-A public unlisted module is the same as an unlisted module.
-{{% /hiddencontent %}}
-
-1. Navigate to the module's page in the Viam Registry, using the link to the module.
-
-1. Find the **Unlisted module usage** section.
-
-1. Copy the module configuration JSON snippet.
-
-1. Navigate to the **CONFIGURE** tab of the machine you want to configure.
-
-1. Switch to **JSON** mode.
-
-1. Paste the copied module configuration into your `modules` array.
-
-1. Copy the model configuration snippet for the model you want to use, and add it to your `components` or `services` array (as appropriate).
- For example:
-
- {{< tabs >}}
- {{% tab name="Example" %}}
-
-```json {class="line-numbers linkable-line-numbers"}
-"components": [
- {
- "name": "sensor-1",
- "api": "rdk:component:sensor",
- "model": "jessamy:hello-world:hello-camera",
- "attributes": {}
- },
- ...
-],
-"modules": [
- {
- "type": "registry",
- "name": "hello-world",
- "module_id": "jessamy:hello-world",
- "version": "latest"
- }
-]
-```
-
-{{% /tab %}}
-{{% tab name="Template" %}}
-
-```json {class="line-numbers linkable-line-numbers"}
-"": [
- {
- "name": "",
- "api": "::",
- "model": "::",
- "attributes": {}
- }
-],
-"modules": [
- {
- "type": "registry",
- "name": "",
- "module_id": ":",
- "version": "latest"
- }
-]
-```
-
-{{% /tab %}}
-{{< /tabs >}}
-
-### Module meta.json configuration
-
-Each module must have a `meta.json` file that defines the module's properties. This file includes information about the module's ID, visibility, and other features.
-
-Example `meta.json` file:
-
-```json
-{
- "module_id": "your-namespace:your-module",
- "visibility": "public",
- "url": "https://github.com/your-org/your-repo",
- "description": "Your module description",
- "entrypoint": "run.sh",
- "first_run": "setup.sh",
- "applications": [
- {
- "name": "your-app-name",
- "type": "single_machine",
- "entrypoint": "dist/index.html",
- "fragmentIds": [],
- "logoPath": "static/logo.png",
- "customizations": {
- "machinePicker": {
- "heading": "Air monitoring dashboard",
- "subheading": "Sign in and select your devices to view your air quality metrics in a dashboard."
- }
- }
- }
- ]
-}
-```
-
-
-| Property | Type | Description |
-| ------------ | ------ | ----------- |
-| `module_id` | string | The module ID, which includes either the module namespace or organization ID, followed by its name: `:` or `:`. The `module_id` uniquely identifies your module. |
-| `visibility` | string | Whether the module is accessible only to members of your organization (`private`), visible to all Viam users (`public`), or unlisted (`public_unlisted`). |
-| `url` | string | The URL of the GitHub repository containing the source code of the module. Required for cloud build. Optional for local modules. |
-| `description` | string | The description of your module and what it provides. |
-| `models` | array | Deprecated. An array of objects describing the models provided by your module. Specified models are ignored. Models are inferred from the module itself. |
-| `entrypoint` | string | The name of the file that starts your module. This can be a compiled executable or a script. Required if you are shipping a model. |
-| `first_run` | string | The path to a script or binary that `viam-server` executes during the setup phase. It executes once when `viam-server` receives a new configuration, and only once per module or per version of the module. |
-
-For modules that include [Viam applications](/operate/control/viam-applications/), you can add the `applications` field.
-The `applications` field is an array of application objects with the following properties:
-
-
-| Property | Type | Description |
-| ------------ | ------ | ----------- |
-| `name` | string | The name of your application, which will be used in the URL (`name.publicnamespace.viamapps.com`) |
-| `type` | string | The type of application: `"single_machine"` or `"multi_machine"`. Whether the application can access and operate one machine or multiple machines. |
-| `entrypoint` | string | The path to the HTML entry point for your application |
-| `fragmentIds` | []string | Specify the fragment or fragments that a machine must contain to be selectable from the machine picker screen. Only for single machine applications. |
-| `logoPath` | string | The URL or the relative path to the logo to display on the machine picker screen for a single machine application. |
-| `customizations` | object | Override the branding heading and subheading to display on the authentication screen for single machine applications:
`heading`: Override the heading. May not be longer than 60 characters.
`subheading`: Override the subheading. May not be longer than 256 characters.
Example: `{ "heading": "Air monitoring dashboard", "subheading": "Sign in and select your devices to view your air quality metrics in a dashboard" }`. |
-
-For more information about Viam applications, see the [Viam applications documentation](/operate/control/viam-applications/).
diff --git a/docs/operate/modules/advanced/platform-apis.md b/docs/operate/modules/advanced/platform-apis.md
deleted file mode 100644
index 7a05866749..0000000000
--- a/docs/operate/modules/advanced/platform-apis.md
+++ /dev/null
@@ -1,310 +0,0 @@
----
-title: "Access platform APIs from within a module"
-linkTitle: "Use platform APIs"
-weight: 25
-layout: "docs"
-type: "docs"
-description: "Write your validate and reconfigure functions to handle dependencies in your custom modular resource."
-aliases:
- - /operate/modules/create-module/platform-apis/
-date: "2025-11-05"
----
-
-To use the platform or machine APIs, you must authenticate using API keys.
-
-- [Use platform APIs from a module](#use-platform-apis-from-a-module)
-- [Use the machine management API from a module](#use-the-machine-management-api-from-a-module)
-
-## Use platform APIs from a module
-
-The following steps show you how to use the following APIs from a module:
-
-- [Fleet management (`app_client`)](/reference/apis/fleet/)
-- [Data client (`data_client`)](/reference/apis/data-client/)
-- [ML training (`ml_training_client`)](/reference/apis/ml-training-client/)
-- [Billing (`billing_client`)](/reference/apis/billing-client/)
-
-{{< tabs >}}
-{{% tab name="Python" %}}
-
-1. Add the following imports:
-
- ```python {class="line-numbers linkable-line-numbers"}
- import os
- from viam.app.viam_client import ViamClient
- from viam.app.app_client import AppClient
- from viam.app.data_client import DataClient
- from viam.app.ml_training_client import MLTrainingClient
- from viam.app.billing_client import BillingClient
- ```
-
-1. Add the `viam_client` and other clients to the resource class:
-
- ```python {class="line-numbers linkable-line-numbers"}
- class TestSensor(Sensor, EasyResource):
- viam_client: Optional[ViamClient] = None
- app_client: Optional[AppClient] = None
- data_client: Optional[DataClient] = None
- ml_training_client: Optional[MLTrainingClient] = None
- billing_client: Optional[BillingClient] = None
-
- # ...
- ```
-
-1. Initialize the clients and use them:
-
- ```python {class="line-numbers linkable-line-numbers"}
- async def some_module_function(self):
- # Ensure there is only one viam_client connection
- if not self.viam_client:
- self.viam_client = await ViamClient.create_from_env_vars(dial_options)
-
- self.app_client = self.viam_client.app_client
- self.data_client = self.viam_client.data_client
- self.ml_training_client = self.viam_client.ml_training_client
- self.billing_client = self.viam_client.billing_client
- # Use the clients in your module
- locations = await self.app_client.list_locations(os.environ.get("VIAM_PRIMARY_ORG_ID"))
- ```
-
-{{% /tab %}}
-{{% tab name="Go" %}}
-
-1. Add the following imports:
-
- ```go {class="line-numbers linkable-line-numbers"}
- "os"
- "go.viam.com/rdk/app"
- ```
-
-1. Add the `viam_client` and other clients to the resource class:
-
- ```go {class="line-numbers linkable-line-numbers"}
- type testPlatformApisGoModuleTestDataClient struct {
- resource.AlwaysRebuild
-
- name resource.Name
-
- logger logging.Logger
- cfg *Config
-
- cancelCtx context.Context
- cancelFunc func()
-
- viamClient *app.ViamClient
- appClient *app.AppClient
- dataClient *app.DataClient
- mlTrainingClient *app.MLTrainingClient
- billingClient *app.BillingClient
- }
- ```
-
-1. Initialize the clients and use them:
-
- ```go {class="line-numbers linkable-line-numbers"}
- func (s *exampleModuleResource) SomeModuleFunction(ctx context.Context, extra map[string]interface{}) (error) {
- if s.viamClient == nil {
- var err error
- s.viamClient, err = app.CreateViamClientFromEnvVars(ctx, &app.Options{}, s.logger)
- if err != nil {
- return nil, err
- }
- s.appClient = s.viamClient.AppClient()
- s.dataClient = s.viamClient.DataClient()
- s.mlTrainingClient = s.viamClient.MLTrainingClient()
- s.billingClient = s.viamClient.BillingClient()
- }
- locations, err := s.appClient.ListLocations(ctx, os.Getenv("VIAM_PRIMARY_ORG_ID"))
- if err != nil {
- return nil, err
- }
-
- // ...
-
- }
- ```
-
-{{% /tab %}}
-{{< /tabs >}}
-
-### Elevate access
-
-The [module environment variables](/operate/modules/advanced/module-configuration/) `VIAM_API_KEY` and `VIAM_API_KEY_ID` provide [machine owner access](/manage/manage/rbac/) for the machine the module is running on.
-
-If you need a higher level of access, you can pass API keys as part of the module configuration:
-
-1. Create an API key with the appropriate [permissions](/manage/manage/rbac/) from your organization settings page.
-1. Add the API key and API key ID values to the module configuration:
-
- ```json
- {
- "modules": [
- {
- "type": "registry",
- "name": "example-module",
- "module_id": "naomi:example-module",
- "version": "latest",
- "env": {
- "VIAM_API_KEY": "abcdefg987654321abcdefghi",
- "VIAM_API_KEY_ID": "1234abcd-123a-987b-1234567890abc"
- }
- }
- ]
- }
- ```
-
- This changes the environment variables `VIAM_API_KEY` and `VIAM_API_KEY_ID` from the default to the provided ones.
-
-## Use the machine management API from a module
-
-To use the [machine management (`robot_client`) API](/reference/apis/robot/), you must get the machine's FQDN and API keys from the module environment variables.
-
-{{< tabs >}}
-{{% tab name="Python" %}}
-
-1. Add the following imports and the `create_robot_client_from_module` method:
-
- ```python {class="line-numbers linkable-line-numbers"}
- # Add imports
- import os
- from viam.robot.client import RobotClient
-
- # For robot client, you can also use the machine's FQDN:
- async def create_robot_client_from_module():
- # Get API credentials from module environment variables
- api_key = os.environ.get("VIAM_API_KEY")
- api_key_id = os.environ.get("VIAM_API_KEY_ID")
- machine_fqdn = os.environ.get("VIAM_MACHINE_FQDN")
-
- if not api_key or not api_key_id or not machine_fqdn:
- raise Exception("VIAM_API_KEY, VIAM_API_KEY_ID, and " +
- "VIAM_MACHINE_FQDN " +
- "environment variables are required")
-
- # Create robot client options with API key authentication
- opts = RobotClient.Options.with_api_key(
- api_key=api_key,
- api_key_id=api_key_id
- )
-
- # Create RobotClient using the machine's FQDN
- robot_client = await RobotClient.at_address(machine_fqdn, opts)
-
- return robot_client
- ```
-
-1. Add the `robot_client` or other clients to the resource class:
-
- ```python {class="line-numbers linkable-line-numbers"}
- class TestSensor(Sensor, EasyResource):
- robot_client: Optional[RobotClient] = None
- # ...
- ```
-
-1. Initialize the client and use it:
-
- ```python {class="line-numbers linkable-line-numbers"}
- async def some_module_function(self):
- # Ensure there is only one robot client
- if not self.robot_client:
- self.robot_client = await create_robot_client_from_module()
- # Use the robot client
- resources = [str(name) for name in self.robot_client.resource_names]
- ```
-
-{{% /tab %}}
-{{% tab name="Go" %}}
-
-1. Add the following imports and the `createRobotClientFromModule` function:
-
- ```go {class="line-numbers linkable-line-numbers"}
- "os"
- "go.viam.com/rdk/robot/client"
- "go.viam.com/utils/rpc"
-
- func createRobotClientFromModule(ctx context.Context, logger logging.Logger) (*client.RobotClient, error) {
- robotClient, err := client.New(
- ctx,
- os.Getenv("VIAM_MACHINE_FQDN"),
- logger,
- client.WithDialOptions(rpc.WithEntityCredentials(
- os.Getenv("VIAM_API_KEY_ID"),
- rpc.Credentials{
- Type: rpc.CredentialsTypeAPIKey,
- Payload: os.Getenv("VIAM_API_KEY"),
- })),
- )
- if err != nil {
- return nil, err
- }
- return robotClient, nil
- }
- ```
-
-1. Add the `viam_client` and other clients to the resource class:
-
- ```go {class="line-numbers linkable-line-numbers"}
- type testPlatformApisGoModuleTestDataClient struct {
- resource.AlwaysRebuild
-
- name resource.Name
-
- logger logging.Logger
- cfg *Config
-
- cancelCtx context.Context
- cancelFunc func()
-
- machine *client.RobotClient
- }
- ```
-
-1. Initialize the clients and use them:
-
-```go {class="line-numbers linkable-line-numbers"}
-func (s *exampleModuleResource) SomeModuleFunction(ctx context.Context, extra map[string]interface{}) (error) {
- if s.machine == nil {
- var err error
- s.machine, err = createRobotClientFromModule(ctx, s.logger)
- if err != nil {
- return nil, err
- }
- defer s.machine.Close(context.Background())
- }
- resources := s.machine.ResourceNames()
-
- // ...
-
-}
-```
-
-{{% /tab %}}
-{{% /tabs %}}
-
-### Elevate access
-
-The [module environment variables](/operate/modules/advanced/module-configuration/) `VIAM_API_KEY` and `VIAM_API_KEY_ID` provide [machine owner access](/manage/manage/rbac/) for the machine the module is running on.
-
-If you need a higher level of access to access other machines, you can pass API keys as part of the module configuration:
-
-1. Create an API key with the appropriate [permissions](/manage/manage/rbac/) from your organization settings page.
-1. Add the API key and API key ID values to the module configuration:
-
- ```json
- {
- "modules": [
- {
- "type": "registry",
- "name": "example-module",
- "module_id": "naomi:example-module",
- "version": "latest",
- "env": {
- "VIAM_API_KEY": "abcdefg987654321abcdefghi",
- "VIAM_API_KEY_ID": "1234abcd-123a-987b-1234567890abc"
- }
- }
- ]
- }
- ```
-
- This changes the environment variables `VIAM_API_KEY` and `VIAM_API_KEY_ID` from the default to the provided ones.
diff --git a/docs/operate/modules/component-apis/_index.md b/docs/operate/modules/component-apis/_index.md
deleted file mode 100644
index 831f9008aa..0000000000
--- a/docs/operate/modules/component-apis/_index.md
+++ /dev/null
@@ -1,8 +0,0 @@
----
-linkTitle: "Component APIs"
-title: "Component APIs"
-weight: 200
-layout: "empty"
-type: "docs"
-empty_node: true
----
diff --git a/docs/operate/modules/component-apis/arm.md b/docs/operate/modules/component-apis/arm.md
deleted file mode 100644
index c9d3582acb..0000000000
--- a/docs/operate/modules/component-apis/arm.md
+++ /dev/null
@@ -1,10 +0,0 @@
----
-title: "Arm Component"
-linkTitle: "Arm"
-weight: 10
-type: "docs"
-layout: "empty"
-canonical: "/dev/reference/apis/components/arm/"
-aliases:
- - /operate/modules/supported-hardware/arm/
----
diff --git a/docs/operate/modules/component-apis/base.md b/docs/operate/modules/component-apis/base.md
deleted file mode 100644
index cbcf29cec4..0000000000
--- a/docs/operate/modules/component-apis/base.md
+++ /dev/null
@@ -1,10 +0,0 @@
----
-title: "Base Component"
-linkTitle: "Base"
-weight: 10
-type: "docs"
-layout: "empty"
-canonical: "/dev/reference/apis/components/base/"
-aliases:
- - /operate/modules/supported-hardware/base/
----
diff --git a/docs/operate/modules/component-apis/board.md b/docs/operate/modules/component-apis/board.md
deleted file mode 100644
index 35ed931fa3..0000000000
--- a/docs/operate/modules/component-apis/board.md
+++ /dev/null
@@ -1,10 +0,0 @@
----
-title: "Board Component"
-linkTitle: "Board"
-weight: 10
-type: "docs"
-layout: "empty"
-canonical: "/dev/reference/apis/components/board/"
-aliases:
- - /operate/modules/supported-hardware/board/
----
diff --git a/docs/operate/modules/component-apis/button.md b/docs/operate/modules/component-apis/button.md
deleted file mode 100644
index 63c950eb83..0000000000
--- a/docs/operate/modules/component-apis/button.md
+++ /dev/null
@@ -1,10 +0,0 @@
----
-title: "Button Component"
-linkTitle: "Button"
-weight: 10
-type: "docs"
-layout: "empty"
-canonical: "/dev/reference/apis/components/button/"
-aliases:
- - /operate/modules/supported-hardware/button/
----
diff --git a/docs/operate/modules/component-apis/camera.md b/docs/operate/modules/component-apis/camera.md
deleted file mode 100644
index 7446851339..0000000000
--- a/docs/operate/modules/component-apis/camera.md
+++ /dev/null
@@ -1,10 +0,0 @@
----
-title: "Camera Component"
-linkTitle: "Camera"
-weight: 10
-type: "docs"
-layout: "empty"
-canonical: "/dev/reference/apis/components/camera/"
-aliases:
- - /operate/modules/supported-hardware/camera/
----
diff --git a/docs/operate/modules/component-apis/encoder.md b/docs/operate/modules/component-apis/encoder.md
deleted file mode 100644
index 60d2323ecd..0000000000
--- a/docs/operate/modules/component-apis/encoder.md
+++ /dev/null
@@ -1,10 +0,0 @@
----
-title: "Encoder Component"
-linkTitle: "Encoder"
-weight: 10
-type: "docs"
-layout: "empty"
-canonical: "/dev/reference/apis/components/encoder/"
-aliases:
- - /operate/modules/supported-hardware/encoder/
----
diff --git a/docs/operate/modules/component-apis/gantry.md b/docs/operate/modules/component-apis/gantry.md
deleted file mode 100644
index 4a1d1f90e2..0000000000
--- a/docs/operate/modules/component-apis/gantry.md
+++ /dev/null
@@ -1,10 +0,0 @@
----
-title: "Gantry Component"
-linkTitle: "Gantry"
-weight: 10
-type: "docs"
-layout: "empty"
-canonical: "/dev/reference/apis/components/gantry/"
-aliases:
- - /operate/modules/supported-hardware/gantry/
----
diff --git a/docs/operate/modules/component-apis/generic.md b/docs/operate/modules/component-apis/generic.md
deleted file mode 100644
index 0018d82b8e..0000000000
--- a/docs/operate/modules/component-apis/generic.md
+++ /dev/null
@@ -1,10 +0,0 @@
----
-title: "Generic Component"
-linkTitle: "Generic"
-weight: 20
-type: "docs"
-layout: "empty"
-canonical: "/dev/reference/apis/components/generic/"
-aliases:
- - /operate/modules/supported-hardware/generic/
----
diff --git a/docs/operate/modules/component-apis/gripper.md b/docs/operate/modules/component-apis/gripper.md
deleted file mode 100644
index 1321df3bad..0000000000
--- a/docs/operate/modules/component-apis/gripper.md
+++ /dev/null
@@ -1,10 +0,0 @@
----
-title: "Gripper Component"
-linkTitle: "Gripper"
-weight: 10
-type: "docs"
-layout: "empty"
-canonical: "/dev/reference/apis/components/gripper/"
-aliases:
- - /operate/modules/supported-hardware/gripper/
----
diff --git a/docs/operate/modules/component-apis/input-controller.md b/docs/operate/modules/component-apis/input-controller.md
deleted file mode 100644
index c7ba0ee7a5..0000000000
--- a/docs/operate/modules/component-apis/input-controller.md
+++ /dev/null
@@ -1,10 +0,0 @@
----
-title: "Input Controller Component"
-linkTitle: "Input Controller"
-weight: 10
-type: "docs"
-layout: "empty"
-canonical: "/dev/reference/apis/components/input-controller/"
-aliases:
- - /operate/modules/supported-hardware/input-controller/
----
diff --git a/docs/operate/modules/component-apis/motor.md b/docs/operate/modules/component-apis/motor.md
deleted file mode 100644
index 27a697bde0..0000000000
--- a/docs/operate/modules/component-apis/motor.md
+++ /dev/null
@@ -1,10 +0,0 @@
----
-title: "Motor Component"
-linkTitle: "Motor"
-weight: 10
-type: "docs"
-layout: "empty"
-canonical: "/dev/reference/apis/components/motor/"
-aliases:
- - /operate/modules/supported-hardware/motor/
----
diff --git a/docs/operate/modules/component-apis/movement-sensor.md b/docs/operate/modules/component-apis/movement-sensor.md
deleted file mode 100644
index 64890d2ccf..0000000000
--- a/docs/operate/modules/component-apis/movement-sensor.md
+++ /dev/null
@@ -1,10 +0,0 @@
----
-title: "Movement Sensor Component"
-linkTitle: "Movement Sensor"
-weight: 10
-type: "docs"
-layout: "empty"
-canonical: "/dev/reference/apis/components/movement-sensor/"
-aliases:
- - /operate/modules/orted-hardware/movement-sensor/
----
diff --git a/docs/operate/modules/component-apis/power-sensor.md b/docs/operate/modules/component-apis/power-sensor.md
deleted file mode 100644
index ccddcce2ea..0000000000
--- a/docs/operate/modules/component-apis/power-sensor.md
+++ /dev/null
@@ -1,10 +0,0 @@
----
-title: "Power Sensor Component"
-linkTitle: "Power Sensor"
-weight: 10
-type: "docs"
-layout: "empty"
-canonical: "/dev/reference/apis/components/power-sensor/"
-aliases:
- - /operate/modules/orted-hardware/power-sensor/
----
diff --git a/docs/operate/modules/component-apis/sensor.md b/docs/operate/modules/component-apis/sensor.md
deleted file mode 100644
index 8d2bef38eb..0000000000
--- a/docs/operate/modules/component-apis/sensor.md
+++ /dev/null
@@ -1,10 +0,0 @@
----
-title: "Sensor Component"
-linkTitle: "Sensor"
-weight: 10
-type: "docs"
-layout: "empty"
-canonical: "/dev/reference/apis/components/sensor/"
-aliases:
- - /operate/modules/supported-hardware/sensor/
----
diff --git a/docs/operate/modules/component-apis/servo.md b/docs/operate/modules/component-apis/servo.md
deleted file mode 100644
index 9cf4092219..0000000000
--- a/docs/operate/modules/component-apis/servo.md
+++ /dev/null
@@ -1,10 +0,0 @@
----
-title: "Servo Component"
-linkTitle: "Servo"
-weight: 10
-type: "docs"
-layout: "empty"
-canonical: "/dev/reference/apis/components/servo/"
-aliases:
- - /operate/modules/supported-hardware/servo/
----
diff --git a/docs/operate/modules/component-apis/switch.md b/docs/operate/modules/component-apis/switch.md
deleted file mode 100644
index 08d4611408..0000000000
--- a/docs/operate/modules/component-apis/switch.md
+++ /dev/null
@@ -1,10 +0,0 @@
----
-title: "Switch Component"
-linkTitle: "Switch"
-weight: 10
-type: "docs"
-layout: "empty"
-canonical: "/dev/reference/apis/components/switch/"
-aliases:
- - /operate/modules/supported-hardware/switch/
----
diff --git a/docs/operate/modules/configure-modules.md b/docs/operate/modules/configure-modules.md
deleted file mode 100644
index 4541debde6..0000000000
--- a/docs/operate/modules/configure-modules.md
+++ /dev/null
@@ -1,291 +0,0 @@
----
-linkTitle: "Use registry modules"
-title: "Configure registry modules"
-weight: 10
-layout: "docs"
-type: "docs"
-imageAlt: "Configure a Machine"
-images: ["/viam.svg"]
-description: "Use modules from the registry and their contained hardware components and software services on your machine."
-modulescript: true
-aliases:
- - /program/extend/modular-resources/configure/
- - /extend/modular-resources/configure/
- - /modular-resources/configure/
- - /registry/configure/
- - /registry/modular-resources/
- - /configure/
- - /manage/configuration/
- - /build/configure/
- - /registry/
- - /registry/program/extend/modular-resources/configure/
- - /how-tos/use-cases/configure/
- - /use-cases/configure/
- - /operate/get-started/supported-hardware/
- - /operate/modules/supported-hardware/
-date: "2025-11-04"
----
-
-Viam has a registry of {{< glossary_tooltip term_id="module" text="modules" >}} that implement [standardized APIs](/reference/apis/#component-apis) for categories of hardware {{< glossary_tooltip term_id="component" text="components" >}} and software {{< glossary_tooltip term_id="service" text="services" >}}.
-
-If you are using an ESP32 microcontroller, see the [ESP32-specific instructions](/operate/install/setup-micro/#configure-and-test-your-machine) for how to add modules to the firmware build.
-
-## About the Viam Registry of supported hardware and software
-
-The [registry](https://app.viam.com/registry?type=Module) is the storage and distribution system for {{< glossary_tooltip term_id="module" text="modules" >}}.
-Each module can contain any number of {{< glossary_tooltip term_id="resource" text="resources" >}}: components which wrap hardware drivers or services which implement software like ML models.
-
-There are also modules in the registry that do not directly drive any physical hardware, but rather augment physical hardware with another layer of abstraction, or add software functionality such as a chatbot integration.
-
-Some components and services are built into `viam-server`, so you won't find them in the registry.
-
-You can browse all built-in and modular registry components below or on your machine's configuration page.
-
-## Configure hardware or software on your machine
-
-**Prerequisite:** A machine with [`viam-server` installed and connected to the cloud](/operate/install/setup/) and with any hardware physically connected to your machine and powered on.
-
-1. Navigate to your machine's page.
-1. Click the **+** button on your machine's **CONFIGURE** tab.
-
- {{}}
-
-1. Click **Component or service**.
- This opens a search menu for all existing hardware and software drivers.
- Search for and select a _{{< glossary_tooltip term_id="model" text="model" >}}_ that supports your hardware or implements your software.
- For hardware, search by name, model number, or manufacturer name.
- Or try searching by broader category, for example "webcam" or "motor," since some components do not require drivers that are specific to their exact make and model.
-
- {{}}
-
- You can also browse available components in the [Browse supported hardware by component API](#browse-supported-hardware-by-component-api) section and services in the [Browse supported software by service API](#browse-supported-software-by-service-api) section.
-
-1. Follow the instructions in the configuration card to configure the model's attributes.
-
- {{}}
-
- If you need more details, use the link to the module's README.
-
-1. Click the **TEST** panel of the component's configuration card to, for example, view your camera's stream, turn your motor, or see the latest readings from your sensor.
-
- {{}}
-
- If your resource is not working as expected, check the **ERROR LOGS** panel for error messages.
- You can also [read more troubleshooting tips](/manage/troubleshoot/troubleshoot/) or get help from Viam's AI assistant by clicking on the **Ask AI** button.
-
-### How module configuration works
-
-When you add a modular resource _from the registry_, Viam automatically adds the module that provides it at the same time, generating a configuration card for the modular resource and a separate one for the module.
-If you add a built-in resource, Viam only adds a configuration card for the resource itself.
-
-For details on configuring versioning and environment variables for modules, see [Modular Resource and Module Configuration Details](/operate/modules/advanced/module-configuration/).
-
-{{< alert title="Tip: Organize resources into folders" color="tip" >}}
-
-If you have many components and services on one machine, you can add folders to your fragment and use them to organize the resources.
-
-{{< /alert >}}
-
-## Browse supported hardware by component API
-
-The following built-in and modular components are available for computers and SBCs running `viam-server`.
-Configure any of these components on your machine by following the steps in [Configure hardware or software on your machine](#configure-hardware-or-software-on-your-machine).
-
-If you don't find a component that supports your hardware, you can [create a new module](/operate/modules/write-a-driver-module/) to add it to the registry.
-
-{{< tabs >}}
-{{% tab name="All components" %}}
-
-{{}}
-
-{{% /tab %}}
-{{% tab name="Arm" %}}
-
-The following models implement the [arm component API](/reference/apis/components/arm/):
-
-{{}}
-
-{{% /tab %}}
-{{% tab name="Base" %}}
-
-The following models implement the [base component API](/reference/apis/components/base/):
-
-{{}}
-
-{{% /tab %}}
-{{% tab name="Board" %}}
-
-The following models implement the [board component API](/reference/apis/components/board/):
-
-{{}}
-
-{{% /tab %}}
-{{% tab name="Button" %}}
-
-The following models implement the [button component API](/reference/apis/components/button/):
-
-{{}}
-
-{{% /tab %}}
-{{% tab name="Camera" %}}
-
-The following models implement the [camera component API](/reference/apis/components/camera/):
-
-{{}}
-
-{{% /tab %}}
-{{% tab name="Encoder" %}}
-
-The following models implement the [encoder component API](/reference/apis/components/encoder/):
-
-{{}}
-
-{{% /tab %}}
-{{% tab name="Gantry" %}}
-
-The following models implement the [gantry component API](/reference/apis/components/gantry/):
-
-{{}}
-
-{{% /tab %}}
-{{% tab name="Generic" %}}
-
-The following models implement the [generic component API](/reference/apis/components/generic/):
-
-{{}}
-
-{{% /tab %}}
-{{% tab name="Gripper" %}}
-
-The following models implement the [gripper component API](/reference/apis/components/gripper/):
-
-{{}}
-
-{{% /tab %}}
-{{% tab name="Input Controller" %}}
-
-The following models implement the [input controller component API](/reference/apis/components/input-controller/):
-
-{{}}
-
-{{% /tab %}}
-{{% tab name="Motor" %}}
-
-The following models implement the [motor component API](/reference/apis/components/motor/):
-
-{{}}
-
-{{% /tab %}}
-{{% tab name="Movement Sensor" %}}
-
-The following models implement the [movement sensor component API](/reference/apis/components/movement-sensor/):
-
-{{}}
-
-{{% /tab %}}
-{{% tab name="Power Sensor" %}}
-
-The following models implement the [power sensor component API](/reference/apis/components/power-sensor/):
-
-{{}}
-
-{{% /tab %}}
-{{% tab name="Sensor" %}}
-
-The following models implement the [sensor component API](/reference/apis/components/sensor/):
-
-{{}}
-
-{{% /tab %}}
-{{% tab name="Servo" %}}
-
-The following models implement the [servo component API](/reference/apis/components/servo/):
-
-{{}}
-
-{{% /tab %}}
-{{% tab name="Switch" %}}
-
-The following models implement the [switch component API](/reference/apis/components/switch/):
-
-{{}}
-
-{{% /tab %}}
-{{< /tabs >}}
-
-{{% alert title="Support notice" color="note" %}}
-Modules in the list above are officially supported and maintained by Viam only if they are marked as "built-in," or if the first part of their model triplet is `viam`.
-{{% /alert %}}
-
-## Browse supported software by service API
-
-The following built-in and modular services are available for computers and SBCs running `viam-server`.
-Configure any of these services on your machine by following the steps in [Configure hardware or software on your machine](#configure-hardware-or-software-on-your-machine).
-
-If you don't find a service that supports your use case, you can [create a new module](/operate/modules/write-a-driver-module/) to add it to the registry.
-If you are looking to write control logic, see [Run control logic](/operate/modules/write-a-logic-module/) instead.
-
-{{< tabs >}}
-{{% tab name="All services" %}}
-
-{{}}
-
-{{% /tab %}}
-{{% tab name="Vision" %}}
-
-The following models implement the [vision service API](/reference/apis/services/vision/):
-
-{{}}
-
-{{% /tab %}}
-{{% tab name="ML model" %}}
-
-The following models implement the [ML model service API](/reference/apis/services/ml/):
-
-{{}}
-
-{{% /tab %}}
-{{% tab name="Motion" %}}
-
-The following models implement the [motion service API](/reference/apis/services/motion/):
-
-{{}}
-
-{{% /tab %}}
-{{% tab name="Generic" %}}
-
-The following models implement the [generic service API](/reference/apis/services/generic/):
-
-{{}}
-
-{{% /tab %}}
-{{% tab name="SLAM" %}}
-
-The following models implement the [SLAM service API](/reference/apis/services/slam/):
-
-{{}}
-
-{{% /tab %}}
-{{% tab name="Discovery" %}}
-
-The following models implement the [discovery service API](/reference/apis/services/discovery/):
-
-{{}}
-
-{{% /tab %}}
-{{< /tabs >}}
-
-{{% alert title="Support notice" color="note" %}}
-Modules in the list above are officially supported and maintained by Viam only if they are marked as "built-in," or if the first part of their model triplet is `viam`.
-{{% /alert %}}
-
-## Next steps
-
-If you have other hardware or software you wish to use, continue to [Support additional hardware and software](/operate/modules/write-a-driver-module/).
-If you have configured all your hardware and software, you can do a variety of things with your machine:
-
-- [Deploy control logic to run directly on your machines](/operate/modules/write-a-logic-module/)
-- [Write an app](/operate/control/web-app/) to interact with your machines using any of the Viam SDKs
-- [Capture data from your machines](/data/capture-sync/capture-and-sync-data/)
-- [Create a dataset](/data-ai/train/create-dataset/) and [train an AI model](/train/train-a-model/)
-- [Share the configuration across multiple machines](/manage/fleet/reuse-configuration/)
diff --git a/docs/operate/modules/deploy-a-module.md b/docs/operate/modules/deploy-a-module.md
deleted file mode 100644
index e3668aaf97..0000000000
--- a/docs/operate/modules/deploy-a-module.md
+++ /dev/null
@@ -1,508 +0,0 @@
----
-linkTitle: "Deploy a Module"
-title: "Deploy a Module"
-weight: 30
-layout: "docs"
-type: "docs"
-description: "Package, upload, and distribute a module through the Viam registry."
-date: "2025-01-30"
-aliases:
- - /build/development/deploy-a-module/
- - /development/deploy-a-module/
- - /operate/modules/deploy-module/
- - /extend/modular-resources/upload/
- - /modular-resources/upload/
- - /registry/upload/
- - /how-tos/upload-module/
----
-
-A module that only runs locally on your development machine is useful for testing
-but limits what you can do. Deploying through the Viam module registry lets you:
-
-- **Install on any machine** in your organization (or publicly) through the
- Viam app -- no SSH or manual file copying.
-- **Update over the air** -- release a new version and machines pick it up
- automatically within minutes.
-- **Target multiple platforms** -- cloud build compiles for every architecture
- you need so you don't have to cross-compile locally.
-
-## Concepts
-
-### The module registry
-
-The Viam module registry is a package manager for modules. It stores versioned
-module packages and serves them to machines on demand. When you configure a
-module on a machine, `viam-server` downloads the correct version for the
-machine's platform (OS and architecture).
-
-Modules can be:
-
-- **Private** -- visible only to your organization.
-- **Public** -- visible to all Viam users.
-- **Unlisted** -- usable by anyone who knows the module ID, but not shown in
- registry search results.
-
-### meta.json
-
-Every module has a `meta.json` file that describes it to the registry:
-
-- **module_id** -- unique identifier in the format `namespace:module-name`.
-- **visibility** -- `private` or `public`.
-- **description** -- a summary shown in the registry.
-- **models** -- the list of resource models this module provides.
-- **entrypoint** -- the command that starts the module.
-- **build** -- configuration for cloud builds.
-
-### Cloud build
-
-Cloud build uses GitHub Actions to compile your module for multiple platforms
-automatically. When you push a version tag (for example, `v0.1.0`), a workflow builds
-your module for each target architecture, packages it, and uploads it to the
-registry.
-
-### Versioning
-
-The registry uses semantic versioning. Machines can be configured to:
-
-- **Track the latest version** -- automatically update when a new version is
- uploaded (default).
-- **Pin to a specific version** -- stay on a fixed version.
-
-## Steps
-
-### 1. Review meta.json
-
-The generator creates a `meta.json` file in your module directory. Open it and
-review each field:
-
-```json
-{
- "module_id": "my-org:my-sensor-module",
- "visibility": "private",
- "url": "https://github.com/my-org/my-sensor-module",
- "description": "A custom sensor module that reads temperature and humidity from an HTTP endpoint.",
- "models": [
- {
- "api": "rdk:component:sensor",
- "model": "my-org:my-sensor-module:my-sensor"
- }
- ],
- "entrypoint": "run.sh",
- "build": {
- "setup": "./setup.sh",
- "build": "./build.sh",
- "path": "dist/archive.tar.gz",
- "arch": ["linux/amd64", "linux/arm64"]
- }
-}
-```
-
-| Field | Required | Purpose |
-| ------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------- |
-| `$schema` | No | JSON Schema URL for editor validation. Set to `https://dl.viam.dev/module.schema.json`. |
-| `module_id` | Yes | Unique ID in the registry. Format: `namespace:name`. |
-| `visibility` | Yes | Who can see and install the module: `private`, `public`, or `public_unlisted`. |
-| `url` | No | Link to the source code repository. Required for cloud builds. |
-| `description` | Yes | Shown in the registry UI and search results. |
-| `models` | Yes | List of resource models the module provides. Each has `api`, `model`, and optionally `description` and `markdown_link`. |
-| `entrypoint` | Yes | The path to the command that starts the module inside the archive. |
-| `first_run` | No | Path to a setup script that runs once after first install (default timeout: 1 hour). |
-| `markdown_link` | No | Path to a README file (or `README.md#section` anchor) used as the registry description. |
-| `build.setup` | No | Script that installs build dependencies (runs once). |
-| `build.build` | No | Script that compiles and packages the module. |
-| `build.path` | No | Path to the packaged output archive (default: `module.tar.gz`). |
-| `build.arch` | No | Target platforms to build for (default: `["linux/amd64", "linux/arm64"]`). |
-| `build.darwin_deps` | No | Homebrew dependencies for macOS builds (for example, `["go", "pkg-config"]`). |
-
-Visibility options:
-
-- **`private`** -- only your organization can see and use the module.
-- **`public`** -- all Viam users can see and use it. Requires your organization
- to have a public namespace.
-- **`public_unlisted`** -- any user can use the module if they know the ID, but
- it does not appear in registry search results.
-
-Common `api` values:
-
-- `rdk:component:sensor` for sensors
-- `rdk:component:camera` for cameras
-- `rdk:component:motor` for motors
-- `rdk:component:generic` for generic components
-- `rdk:service:vision` for vision services
-
-### 2. Review the generated build scripts
-
-The generator creates build and setup scripts for your module. Review them
-and customize if needed:
-
-{{< tabs >}}
-{{% tab name="Python" %}}
-
-| File | Purpose |
-| ---------- | ---------------------------------------------------- |
-| `setup.sh` | Installs Python dependencies from `requirements.txt` |
-| `build.sh` | Packages the module into a `.tar.gz` archive |
-| `run.sh` | Entrypoint script that starts the module |
-
-If your module has additional build steps (for example, compiling native extensions),
-add them to `build.sh`.
-
-{{% /tab %}}
-{{% tab name="Go" %}}
-
-| File | Purpose |
-| ---------- | --------------------------------------------------------------------- |
-| `setup.sh` | Installs build dependencies (Go modules are typically self-contained) |
-| `build.sh` | Cross-compiles the binary and packages it into a `.tar.gz` archive |
-| `Makefile` | Local build targets |
-
-The generated `build.sh` uses `GOOS` and `GOARCH` environment variables to
-cross-compile for the target platform. Cloud build sets these automatically.
-
-{{% /tab %}}
-{{< /tabs >}}
-
-Make sure all scripts are executable:
-
-```bash
-chmod +x setup.sh build.sh run.sh
-```
-
-### 3. Deploy with cloud build (recommended)
-
-Cloud build is the recommended way to deploy modules. It uses GitHub Actions to
-compile your module for every target platform automatically, so you don't need
-to cross-compile locally. This eliminates a common class of errors where a
-binary built for one architecture (for example, `linux/amd64`) is uploaded for a
-different one (for example, `linux/arm64`), resulting in exec format errors on the
-target machine.
-
-If this is your first time deploying this module, you need to create it in the registry before uploading:
-
-```bash
-viam module create --name --public-namespace
-```
-
-This registers the module ID so the registry knows about it. You only need to do this once, after that, builds and uploads will work.
-If you used viam module generate, this step may have been done for you automatically.
-
-The generator creates the workflow file at
-`.github/workflows/deploy.yml`. To use it:
-
-**Push your code to GitHub:**
-
-```bash
-cd my-sensor-module
-git init
-git add .
-git commit -m "Initial module code"
-git remote add origin https://github.com/my-org/my-sensor-module.git
-git push -u origin main
-```
-
-**Add Viam credentials as GitHub secrets:**
-
-1. In the [Viam app](https://app.viam.com), go to your organization's settings.
-2. Create an API key with organization-level access (or use an existing one).
-3. In your GitHub repository, go to **Settings > Secrets and variables >
- Actions**.
-4. Add two secrets:
- - `VIAM_KEY_ID` -- your API key ID
- - `VIAM_KEY_VALUE` -- your API key
-
-**Tag a release to trigger the build:**
-
-```bash
-git tag v0.1.0
-git push origin v0.1.0
-```
-
-The GitHub Action runs automatically. Monitor progress in the **Actions** tab
-of your GitHub repository. When it completes, your module is in the registry
-and ready to install on any machine.
-
-You can also trigger a cloud build from the CLI:
-
-```bash
-viam module build start
-```
-
-{{< alert title="Non-main default branches" color="tip" >}}
-Cloud build expects your repository's default branch to be `main`. If your
-repository uses a different default branch (for example, `master`), use the `--ref`
-flag:
-
-```bash
-viam module build start --ref master
-```
-
-{{< /alert >}}
-
-### 4. Deploy manually (alternative)
-
-If you cannot use cloud build, you can build and upload from the command line.
-
-{{< alert title="You must build for the target platform" color="caution" >}}
-When you upload manually, the binary in your archive must already be compiled
-for the target machine's OS and architecture. If you build on an x86 laptop
-and upload for `linux/arm64` without cross-compiling, the module will fail
-with an exec format error on ARM machines (like Raspberry Pi).
-
-Cloud build handles this automatically. If you deploy manually, you must
-cross-compile yourself or build on a machine with the target architecture.
-{{< /alert >}}
-
-**Build locally:**
-
-{{< tabs >}}
-{{% tab name="Python" %}}
-
-```bash
-cd my-sensor-module
-bash build.sh
-```
-
-Python modules don't require cross-compilation since they run in an
-interpreter, but native dependencies (C extensions) may need to be built for
-the target platform.
-
-{{% /tab %}}
-{{% tab name="Go" %}}
-
-```bash
-cd my-sensor-module
-# Cross-compile for the target platform
-GOOS=linux GOARCH=arm64 go build -o dist/module cmd/module/main.go
-tar -czf dist/archive.tar.gz -C dist module
-```
-
-Set `GOARCH` to match your target machine: `amd64` for x86_64, `arm64` for
-ARM (Raspberry Pi 4, Jetson, etc.).
-
-{{% /tab %}}
-{{< /tabs >}}
-
-**Upload to the registry:**
-
-```bash
-viam module upload \
- --version=0.1.0 \
- --platform=linux/arm64 \
- dist/archive.tar.gz
-```
-
-To support multiple platforms, cross-compile and upload once per platform:
-
-```bash
-# Build and upload for amd64
-GOOS=linux GOARCH=amd64 go build -o dist/module cmd/module/main.go
-tar -czf dist/archive.tar.gz -C dist module
-viam module upload --version=0.1.0 --platform=linux/amd64 dist/archive.tar.gz
-
-# Build and upload for arm64
-GOOS=linux GOARCH=arm64 go build -o dist/module cmd/module/main.go
-tar -czf dist/archive.tar.gz -C dist module
-viam module upload --version=0.1.0 --platform=linux/arm64 dist/archive.tar.gz
-```
-
-### 5. Configure the module on a machine
-
-Once your module is in the registry, any machine in your organization can use it.
-
-1. In the [Viam app](https://app.viam.com), navigate to your machine's
- **CONFIGURE** tab.
-2. Click **+** and select **Configuration block**.
-3. Search for your module by name or browse the registry.
-4. Add the module and create a component (or service) instance.
-5. Name the component and configure the attributes your module expects:
-
-```json
-{
- "source_url": "https://api.example.com/sensor/data"
-}
-```
-
-7. Click **Save**.
-
-`viam-server` downloads the module from the registry, starts it, and makes the
-component available. Test it from the **CONTROL** tab.
-
-### 6. Manage versions
-
-**Release a new version:**
-
-```bash
-git add .
-git commit -m "Add humidity calibration offset"
-git tag v0.2.0
-git push origin main v0.2.0
-```
-
-If using cloud build, the workflow runs automatically. For manual upload:
-
-```bash
-viam module upload --version=0.2.0 --platform=linux/amd64 dist/archive.tar.gz
-```
-
-**Automatic updates:** By default, machines track the latest version. When you
-upload `v0.2.0`, all machines update automatically within a few minutes.
-
-**Pin to a specific version:**
-
-1. In the Viam app, go to the machine's **CONFIGURE** tab.
-2. Find the module in the configuration.
-3. Set the **Version** field to the specific version (for example, `0.1.0`).
-4. Click **Save**.
-
-**View module details:**
-
-You can view version history and details for your module in the
-[Viam registry](https://app.viam.com/registry).
-
-### 7. Keep meta.json in sync with your code
-
-As you add models to your module, you can auto-detect them from a built binary
-instead of editing `meta.json` by hand:
-
-```bash
-viam module update-models --binary ./bin/module
-```
-
-This inspects the binary, discovers registered models, and updates the `models`
-array in `meta.json`.
-
-Then push the updated metadata to the registry:
-
-```bash
-viam module update
-```
-
-### 8. Download a module
-
-To download a module from the registry (for testing or inspection):
-
-```bash
-viam module download --id my-org:my-sensor-module --version 0.1.0 --platform linux/amd64 --destination ./downloaded-module
-```
-
-### Platform constraints
-
-When uploading, you can attach platform constraint tags that restrict which
-machines can use a particular upload. For example, to require Debian:
-
-```bash
-viam module upload \
- --version=0.1.0 \
- --platform=linux/amd64 \
- --tags=distro:debian \
- dist/archive.tar.gz
-```
-
-The machine must report matching platform tags for the constrained upload to be
-selected. If no constraints are specified, the upload is available to all
-machines on that platform.
-
-### Upload limits
-
-The registry enforces these size limits:
-
-| Limit | Value |
-| ------------------------------ | ------ |
-| Compressed package (`.tar.gz`) | 50 GB |
-| Decompressed contents | 250 GB |
-| Single file within package | 25 GB |
-
-Before uploading, the CLI validates that:
-
-- The entrypoint executable exists in the archive
-- The entrypoint has execute permissions
-- No symlinks escape the archive boundaries
-
-Use `--force` to skip these checks (not recommended for production uploads).
-
-## Try It
-
-1. Review the generated `meta.json` and build scripts in your module directory.
-2. Push your code to GitHub and add the Viam API key secrets.
-3. Tag a release (`v0.1.0`) to trigger a cloud build.
-4. Navigate to a machine in the Viam app and add your module from the registry.
-5. Configure a component and set the required attributes.
-6. Open the **CONTROL** tab and verify the component works.
-7. Tag a new release (`v0.2.0`) and verify the machine picks it up
- automatically within a few minutes.
-
-## Troubleshooting
-
-{{< expand "Upload fails with \"not authenticated\"" >}}
-
-- Log in to the Viam CLI: `viam login`.
-- If using an API key, verify it has organization-level access.
-- Check that `VIAM_KEY_ID` and `VIAM_KEY_VALUE` are set correctly in your GitHub
- secrets (for cloud build).
-
-{{< /expand >}}
-
-{{< expand "Upload fails with \"invalid meta.json\"" >}}
-
-- Verify `meta.json` is valid JSON. Run `python -m json.tool meta.json` or
- `jq . meta.json` to check.
-- Confirm the `module_id` matches the format `namespace:module-name`.
-- Ensure all model entries have both `api` and `model` fields.
-
-{{< /expand >}}
-
-{{< expand "Module not appearing in the registry" >}}
-
-- Check the module's visibility. If it is `private`, it only appears for users
- in your organization.
-- Verify the upload completed successfully.
-- The module may take a minute to propagate. Refresh the page and try again.
-
-{{< /expand >}}
-
-{{< expand "Machine cannot find the module" >}}
-
-- Verify the module version supports the machine's platform. If your machine
- runs `linux/arm64` but you only uploaded for `linux/amd64`, the machine
- cannot use it.
-- Check the module version. If the machine is pinned to a nonexistent version,
- it will fail.
-- Confirm the machine is online and connected to the cloud.
-
-{{< /expand >}}
-
-{{< expand "Cloud build fails in GitHub Actions" >}}
-
-- Check the Actions tab in your GitHub repository for the build log.
-- If your repository's default branch is not `main` (for example, it uses `master`),
- use `viam module build start --ref master`. The cloud build system expects
- `main` by default.
-- Verify your `setup.sh` and `build.sh` scripts work locally.
-- Confirm the `build.path` in `meta.json` matches the actual output location.
-- Ensure the GitHub secrets are set and not expired.
-
-{{< /expand >}}
-
-{{< expand "Exec format error on target machine" >}}
-
-This means the binary was compiled for the wrong architecture. For example,
-you built on an x86 laptop but the target machine is ARM (Raspberry Pi).
-
-- Use [cloud build](#3-deploy-with-cloud-build-recommended) to compile for all
- target platforms automatically.
-- If deploying manually, cross-compile with the correct `GOOS` and `GOARCH`
- before uploading. See [Deploy manually](#4-deploy-manually-alternative).
-- Verify the platform flag in your `upload` command matches the binary's
- architecture (for example, `--platform=linux/arm64` for Raspberry Pi 4).
-
-{{< /expand >}}
-
-{{< expand "Module works locally but fails after deployment" >}}
-
-- Check for hard-coded paths, missing environment variables, or dependencies
- installed on your machine but not in the build environment.
-- For Python, verify all dependencies are in `requirements.txt`.
-- For Go, verify the binary is compiled for the correct target architecture.
-- Check the module logs in the **LOGS** tab.
-
-{{< /expand >}}
diff --git a/docs/operate/modules/lifecycle-of-a-module.md b/docs/operate/modules/lifecycle-of-a-module.md
deleted file mode 100644
index 7481a91e58..0000000000
--- a/docs/operate/modules/lifecycle-of-a-module.md
+++ /dev/null
@@ -1,54 +0,0 @@
----
-title: "Lifecycle of a module"
-linkTitle: "Lifecycle of a module"
-weight: 35
-layout: "docs"
-type: "docs"
-description: "Learn about the lifecycle of a module: How and when it starts, shuts down, and reconfigures."
-date: "2025-10-17"
-aliases:
- - /operate/modules/other-hardware/lifecycle-module/
- - /operate/modules/lifecycle-module/
----
-
-Modules run on your machine, alongside `viam-server` as separate processes, communicating with `viam-server` over UNIX sockets.
-
-[`viam-server` manages](/operate/reference/viam-server/) the dependencies, start-up, reconfiguration, [data management](/data/capture-sync/capture-and-sync-data/), and shutdown behavior of your {{< glossary_tooltip term_id="resource" text="modular resources" >}}.
-
-The lifecycle of a module and the resources it provides are as follows:
-
-1. `viam-server` starts, and if it is connected to the internet, it checks for configuration updates.
-
-1. `viam-server` starts any configured modules.
-
-1. When a module initializes, it registers its model or models and associated [APIs](/reference/apis/) with `viam-server`, making the models available for use.
-
-1. For each modular resource configured on the machine, `viam-server` uses the return values of the resource's `validate_config` function to determine the required and optional [dependencies](/operate/modules/advanced/dependencies/) of the resource.
-
-1. If a required dependency is not already running, `viam-server` starts it before starting the resource.
- If a required dependency is not found or fails to start, `viam-server` does not start the resource that depends on it.
-
-1. `viam-server` calls the resource's constructor to build the resource based on its configuration.
- Typically, the constructor calls the `reconfigure` function.
-
-1. Modular resources can fail to start for various reasons:
-
- - a validation error or exception thrown in constructor
- - a validation error or exception thrown during reconfiguration
- - exceeding the [configured timeout limits](/operate/modules/advanced/module-configuration/#environment-variables) (default 5 minutes to start up and 1 minute to reconfigure)
-
- Check the `viam-server` logs for these [errors](/monitor/troubleshoot/#timed-out-waiting-for-module) on the machine's **LOGS** tab.
-
-1. Once the modular resource has started up and finished configuring, it is available for use.
-
-1. If at any point the user changes the configuration of the machine, `viam-server` reconfigures the affected resources within 15 seconds.
-
-1. If `viam-server` attempts to shut down an individual module (for example due to a user disabling a module) and the module does not shut down within 30 seconds, `viam-server` kills the module process.
-
-1. When `viam-server` shuts down, it first attempts to shut down each module process sequentially in no particular order.
- If a module process does not shut down within 30 seconds, it is killed with a `SIGKILL` signal.
- If any modules are still running after 90 seconds, `viam-server` kills them as well.
- This means that if four modules are running and the first three each fail to shut down within 30 seconds each, the fourth is killed immediately at the 90 second mark.
-
-Microcontroller modules function differently and are embedded in the firmware you flash onto your device.
-For more information see [Modules for ESP32](/operate/modules/advanced/micro-module/).
diff --git a/docs/operate/modules/write-a-driver-module/_index.md b/docs/operate/modules/write-a-driver-module/_index.md
deleted file mode 100644
index df9693dc9a..0000000000
--- a/docs/operate/modules/write-a-driver-module/_index.md
+++ /dev/null
@@ -1,1062 +0,0 @@
----
-linkTitle: "Write a Driver Module"
-title: "Write a Hardware Driver Module"
-weight: 20
-layout: "docs"
-type: "docs"
-description: "Build a module that implements a resource API and runs as a separate process."
-date: "2025-01-30"
-aliases:
- - /build/development/write-a-module/
- - /development/write-a-module/
- - /development/write-a-driver-module/
- - /registry/create/
- - /use-cases/create-module/
- - /how-tos/create-module/
- - /how-tos/sensor-module/
- - /registry/advanced/iterative-development/
- - /build/program/extend/modular-resources/
- - /program/extend/modular-resources/
- - /extend/
- - /extend/modular-resources/
- - /extend/modular-resources/create/
- - /build/program/extend/modular-resources/key-concepts/
- - /modular-resources/key-concepts/
- - /modular-resources/
- - /extend/modular-resources/examples/custom-arm/
- - /modular-resources/examples/custom-arm/
- - /registry/examples/custom-arm/
- - /program/extend/modular-resources/examples/
- - /extend/modular-resources/examples/
- - /modular-resources/examples/
- - /registry/examples/
- - /operate/get-started/other-hardware/
- - /operate/get-started/other-hardware/create-module/
- - /operate/modules/other-hardware/create-module/
- - /how-tos/hello-world-module/
- - /operate/get-started/other-hardware/hello-world-module/
- - /operate/modules/create-module/hello-world-module/
- - /operate/modules/supported-hardware/hello-world-module/
- - /operate/modules/support-hardware/
----
-
-You want to use hardware that Viam doesn't support out of the box. A driver
-module integrates it with the platform by implementing a standard resource API
-(sensor, camera, motor, or any other type). Once your hardware speaks a Viam
-API, data capture, TEST cards, the SDKs, and other platform features work
-with it automatically.
-
-A driver module runs as a separate process alongside `viam-server`. It has its
-own dependencies, can crash without affecting `viam-server`, and can be
-packaged and distributed through the Viam registry.
-
-## Concepts
-
-### Choosing a resource API
-
-Viam defines standard APIs for common resource types. Pick the API that best
-matches your hardware or service:
-
-| API | Use when your hardware... | Key methods |
-| ----------------- | ----------------------------------------------------- | ---------------------------------- |
-| `sensor` | Produces readings (temperature, distance, humidity) | `GetReadings` |
-| `camera` | Produces images or point clouds | `GetImages`, `GetPointCloud` |
-| `motor` | Drives rotational or linear motion | `SetPower`, `GoFor`, `Stop` |
-| `servo` | Moves to angular positions | `Move`, `GetPosition` |
-| `board` | Exposes GPIO pins, analog readers, digital interrupts | `GPIOPinByName`, `AnalogByName` |
-| `encoder` | Tracks position or rotation | `GetPosition`, `ResetPosition` |
-| `movement_sensor` | Reports position, orientation, velocity | `GetPosition`, `GetLinearVelocity` |
-| `generic` | Does not fit any of the above | `DoCommand` |
-
-For the full list of component and service APIs, see
-[Resource APIs](/reference/apis/).
-
-Using the right API means data capture, TEST cards, and other platform
-features work with your component automatically.
-
-Every resource also has a `DoCommand` method. Use it for functionality that
-does not map to the standard API methods -- for example, a sensor that also has
-a calibration routine. `DoCommand` accepts and returns arbitrary key-value maps.
-
-### Module lifecycle
-
-Every module goes through a defined lifecycle:
-
-1. **Startup** -- `viam-server` launches the module as a separate process. The
- module registers its models and opens a gRPC connection back to the server.
-2. **Validation** -- For each configured resource, `viam-server` calls the
- model's config validation method to check attributes and declare
- dependencies.
-3. **Creation** -- If validation passes, `viam-server` calls the model's
- constructor with the resolved dependencies.
-4. **Reconfiguration** -- If the user changes the configuration, `viam-server`
- calls the validation method again, then the reconfiguration method.
-5. **Shutdown** -- `viam-server` calls the resource's close method. Clean up
- resources here.
-
-For more detail, see [Module Lifecycle](/operate/modules/lifecycle-of-a-module/).
-
-### Dependencies
-
-Dependencies let your resource use other resources on the same machine. You
-declare dependencies in your config validation method by returning the names of
-resources your module needs. `viam-server` resolves these, ensures the
-depended-on resources are ready, and passes them to your constructor.
-
-## Steps
-
-When writing a module, follow the steps outlined below. To illustrate each step we'll use a sensor module as a worked example. The same patterns
-apply to any resource type -- substitute the appropriate API and methods for
-your use case.
-
-### 1. Generate the module
-
-Run the Viam CLI generator:
-
-```bash
-viam module generate
-```
-
-| Prompt | What to enter | Why |
-| ---------------- | --------------------------- | -------------------------------- |
-| Module name | `my-sensor-module` | A short, descriptive name |
-| Language | `python` or `go` | Your implementation language |
-| Visibility | `private` | Keep it private while developing |
-| Namespace | Your organization namespace | Scopes the module to your org |
-| Resource subtype | `sensor` | The resource API to implement |
-| Model name | `my-sensor` | The model name for your sensor |
-| Register | `yes` | Registers the module with Viam |
-
-The generator creates a complete project with the following files:
-
-{{< tabs >}}
-{{% tab name="Python" %}}
-
-| File | Purpose |
-| ------------------------------ | --------------------------------------------- |
-| `src/main.py` | Entry point -- starts the module server |
-| `src/models/my_sensor.py` | Resource class skeleton -- you will edit this |
-| `requirements.txt` | Python dependencies |
-| `meta.json` | Module metadata for the registry |
-| `setup.sh` | Installs dependencies into a virtualenv |
-| `build.sh` | Packages the module for upload |
-| `.github/workflows/deploy.yml` | CI workflow for cloud builds |
-
-{{% /tab %}}
-{{% tab name="Go" %}}
-
-| File | Purpose |
-| ------------------------------ | ------------------------------------------------------ |
-| `cmd/module/main.go` | Entry point -- starts the module server |
-| `my_sensor_module.go` | Resource implementation skeleton -- you will edit this |
-| `go.mod` | Go module definition |
-| `Makefile` | Build targets |
-| `meta.json` | Module metadata for the registry |
-| `.github/workflows/deploy.yml` | CI workflow for cloud builds |
-
-{{% /tab %}}
-{{< /tabs >}}
-
-### 2. Implement the resource API
-
-Open the generated resource file. The generator creates a class (Python) or
-struct (Go) with stub methods. You need to make three changes:
-
-1. Define your config attributes.
-2. Add validation logic.
-3. Implement the API methods for your resource type.
-
-The following example builds a sensor that reads temperature and humidity from
-a custom HTTP API endpoint. Replace the HTTP call with whatever data source
-your sensor uses.
-
-#### Define your config attributes
-
-Config attributes are the fields a user sets when they configure your component
-in the Viam app. The generator creates an empty config; add a field for each
-attribute your module needs.
-
-{{< tabs >}}
-{{% tab name="Python" %}}
-
-In `src/models/my_sensor.py`, add instance attributes to your class. These will
-be set in the `reconfigure` method:
-
-```python
-class MySensor(Sensor, EasyResource):
- MODEL: ClassVar[Model] = Model(
- ModelFamily("my-org", "my-sensor-module"), "my-sensor"
- )
-
- # Add your config attributes as instance variables
- source_url: str
- poll_interval: float
-```
-
-{{% /tab %}}
-{{% tab name="Go" %}}
-
-In the generated `.go` file, find the empty `Config` struct and add fields.
-Each field needs a `json` tag that matches the attribute name users will set in
-their config JSON:
-
-```go
-type Config struct {
- SourceURL string `json:"source_url"`
- PollInterval float64 `json:"poll_interval"`
-}
-```
-
-{{% /tab %}}
-{{< /tabs >}}
-
-#### Add validation logic
-
-The generator creates an empty validation method. Add checks for required
-fields and return any [dependencies](#dependencies) your module needs.
-
-{{< tabs >}}
-{{% tab name="Python" %}}
-
-Find `validate_config` in your class and add validation:
-
-```python
- @classmethod
- def validate_config(
- cls, config: ComponentConfig
- ) -> Tuple[Sequence[str], Sequence[str]]:
- fields = config.attributes.fields
- if "source_url" not in fields:
- raise Exception("source_url is required")
- if not fields["source_url"].string_value.startswith("http"):
- raise Exception("source_url must be an HTTP or HTTPS URL")
- return [], [] # No required or optional dependencies
-```
-
-{{% /tab %}}
-{{% tab name="Go" %}}
-
-Find the `Validate` method on your `Config` struct and add validation:
-
-```go
-func (cfg *Config) Validate(path string) ([]string, []string, error) {
- if cfg.SourceURL == "" {
- return nil, nil, fmt.Errorf("source_url is required")
- }
- return nil, nil, nil // No required or optional dependencies
-}
-```
-
-{{% /tab %}}
-{{< /tabs >}}
-
-The validation method returns two lists: required dependencies and optional
-dependencies. For a standalone sensor with no dependencies, return empty lists.
-See [Step 5](#5-handle-dependencies) for modules that depend on other
-components.
-
-#### Implement the constructor and reconfigure method
-
-The constructor creates your resource and the reconfigure method updates it when
-the config changes. In Python, the typical pattern is for `new` to call
-`reconfigure` so the config-reading logic lives in one place.
-
-{{< tabs >}}
-{{% tab name="Python" %}}
-
-Update `new` and add a `reconfigure` method:
-
-```python
- @classmethod
- def new(cls, config: ComponentConfig,
- dependencies: Mapping[ResourceName, ResourceBase]) -> Self:
- sensor = cls(config.name)
- sensor.reconfigure(config, dependencies)
- return sensor
-
- def reconfigure(self, config: ComponentConfig,
- dependencies: Mapping[ResourceName, ResourceBase]) -> None:
- fields = config.attributes.fields
- self.source_url = fields["source_url"].string_value
- self.poll_interval = (
- fields["poll_interval"].number_value
- if "poll_interval" in fields
- else 10.0
- )
-```
-
-{{% /tab %}}
-{{% tab name="Go" %}}
-
-Find the generated constructor function. Update it to read your config fields
-and initialize your struct:
-
-```go
-func newMySensor(
- ctx context.Context,
- deps resource.Dependencies,
- conf resource.Config,
- logger logging.Logger,
-) (sensor.Sensor, error) {
- cfg, err := resource.NativeConfig[*Config](conf)
- if err != nil {
- return nil, err
- }
-
- timeout := time.Duration(cfg.PollInterval) * time.Second
- if timeout == 0 {
- timeout = 10 * time.Second
- }
-
- return &MySensor{
- Named: conf.ResourceName().AsNamed(),
- logger: logger,
- sourceURL: cfg.SourceURL,
- client: &http.Client{Timeout: timeout},
- }, nil
-}
-```
-
-You will also need to add fields to the generated struct for any state your
-module needs at runtime:
-
-```go
-type MySensor struct {
- resource.Named
- resource.AlwaysRebuild
- logger logging.Logger
- sourceURL string
- client *http.Client
-}
-```
-
-The generated struct includes `resource.AlwaysRebuild`, which tells
-`viam-server` to destroy and re-create the resource on every config change.
-This is the simplest approach and works well for most modules. For in-place
-reconfiguration, see [Step 6](#6-handle-reconfiguration-optional).
-
-{{% /tab %}}
-{{< /tabs >}}
-
-#### Implement the API method
-
-For a sensor, the key method is `GetReadings`, which returns a map of reading
-names to values. This is the method that data capture calls and your application
-code queries.
-
-The generator creates a stub that returns an error. Replace it with your
-implementation:
-
-{{< tabs >}}
-{{% tab name="Python" %}}
-
-Add a `get_readings` method to your class. The return type is
-`Mapping[str, SensorReading]` (import `SensorReading` from `viam.utils`):
-
-```python
- async def get_readings(
- self,
- *,
- extra: Optional[Mapping[str, Any]] = None,
- timeout: Optional[float] = None,
- **kwargs,
- ) -> Mapping[str, SensorReading]:
- try:
- response = requests.get(self.source_url, timeout=5)
- response.raise_for_status()
- data = response.json()
- return {
- "temperature": data["temp"],
- "humidity": data["humidity"],
- }
- except requests.RequestException as e:
- self.logger.error(f"Failed to read from {self.source_url}: {e}")
- return {"error": str(e)}
-```
-
-{{% /tab %}}
-{{% tab name="Go" %}}
-
-Find the `Readings` method stub and replace it:
-
-```go
-type sensorResponse struct {
- Temp float64 `json:"temp"`
- Humidity float64 `json:"humidity"`
-}
-
-func (s *MySensor) Readings(
- ctx context.Context,
- extra map[string]interface{},
-) (map[string]interface{}, error) {
- resp, err := s.client.Get(s.sourceURL)
- if err != nil {
- s.logger.CErrorw(ctx, "failed to read from source",
- "url", s.sourceURL, "error", err)
- return nil, fmt.Errorf("failed to read from %s: %w", s.sourceURL, err)
- }
- defer resp.Body.Close()
-
- var data sensorResponse
- if err := json.NewDecoder(resp.Body).Decode(&data); err != nil {
- return nil, fmt.Errorf("failed to decode response: %w", err)
- }
-
- return map[string]interface{}{
- "temperature": data.Temp,
- "humidity": data.Humidity,
- }, nil
-}
-```
-
-{{% /tab %}}
-{{< /tabs >}}
-
-{{< expand "View the complete resource file" >}}
-
-For reference, here is the complete resource file after all the changes above.
-
-{{< tabs >}}
-{{% tab name="Python" %}}
-
-`src/models/my_sensor.py`:
-
-```python
-import requests
-from typing import Any, ClassVar, Mapping, Optional, Sequence, Self, Tuple
-
-from viam.components.sensor import Sensor
-from viam.proto.app.robot import ComponentConfig
-from viam.proto.common import ResourceName
-from viam.resource.base import ResourceBase
-from viam.resource.easy_resource import EasyResource
-from viam.resource.types import Model, ModelFamily
-from viam.utils import SensorReading
-
-
-class MySensor(Sensor, EasyResource):
- """A custom sensor that reads from an HTTP endpoint."""
-
- MODEL: ClassVar[Model] = Model(
- ModelFamily("my-org", "my-sensor-module"), "my-sensor"
- )
-
- source_url: str
- poll_interval: float
-
- @classmethod
- def new(cls, config: ComponentConfig,
- dependencies: Mapping[ResourceName, ResourceBase]) -> Self:
- sensor = cls(config.name)
- sensor.reconfigure(config, dependencies)
- return sensor
-
- @classmethod
- def validate_config(
- cls, config: ComponentConfig
- ) -> Tuple[Sequence[str], Sequence[str]]:
- fields = config.attributes.fields
- if "source_url" not in fields:
- raise Exception("source_url is required")
- if not fields["source_url"].string_value.startswith("http"):
- raise Exception("source_url must be an HTTP or HTTPS URL")
- return [], []
-
- def reconfigure(self, config: ComponentConfig,
- dependencies: Mapping[ResourceName, ResourceBase]) -> None:
- fields = config.attributes.fields
- self.source_url = fields["source_url"].string_value
- self.poll_interval = (
- fields["poll_interval"].number_value
- if "poll_interval" in fields
- else 10.0
- )
-
- async def get_readings(
- self,
- *,
- extra: Optional[Mapping[str, Any]] = None,
- timeout: Optional[float] = None,
- **kwargs,
- ) -> Mapping[str, SensorReading]:
- try:
- response = requests.get(self.source_url, timeout=5)
- response.raise_for_status()
- data = response.json()
- return {
- "temperature": data["temp"],
- "humidity": data["humidity"],
- }
- except requests.RequestException as e:
- self.logger.error(f"Failed to read from {self.source_url}: {e}")
- return {"error": str(e)}
-
- async def close(self):
- self.logger.info("Shutting down MySensor")
-```
-
-{{% /tab %}}
-{{% tab name="Go" %}}
-
-`my_sensor_module.go`:
-
-```go
-package mysensormodule
-
-import (
- "context"
- "encoding/json"
- "fmt"
- "net/http"
- "time"
-
- "go.viam.com/rdk/components/sensor"
- "go.viam.com/rdk/logging"
- "go.viam.com/rdk/resource"
-)
-
-var Model = resource.NewModel("my-org", "my-sensor-module", "my-sensor")
-
-type Config struct {
- SourceURL string `json:"source_url"`
- PollInterval float64 `json:"poll_interval"`
-}
-
-func (cfg *Config) Validate(path string) ([]string, []string, error) {
- if cfg.SourceURL == "" {
- return nil, nil, fmt.Errorf("source_url is required")
- }
- return nil, nil, nil
-}
-
-func init() {
- resource.RegisterComponent(sensor.API, Model,
- resource.Registration[sensor.Sensor, *Config]{
- Constructor: newMySensor,
- },
- )
-}
-
-type MySensor struct {
- resource.Named
- resource.AlwaysRebuild
- logger logging.Logger
- sourceURL string
- client *http.Client
-}
-
-func newMySensor(
- ctx context.Context,
- deps resource.Dependencies,
- conf resource.Config,
- logger logging.Logger,
-) (sensor.Sensor, error) {
- cfg, err := resource.NativeConfig[*Config](conf)
- if err != nil {
- return nil, err
- }
-
- timeout := time.Duration(cfg.PollInterval) * time.Second
- if timeout == 0 {
- timeout = 10 * time.Second
- }
-
- return &MySensor{
- Named: conf.ResourceName().AsNamed(),
- logger: logger,
- sourceURL: cfg.SourceURL,
- client: &http.Client{Timeout: timeout},
- }, nil
-}
-
-type sensorResponse struct {
- Temp float64 `json:"temp"`
- Humidity float64 `json:"humidity"`
-}
-
-func (s *MySensor) Readings(
- ctx context.Context,
- extra map[string]interface{},
-) (map[string]interface{}, error) {
- resp, err := s.client.Get(s.sourceURL)
- if err != nil {
- s.logger.CErrorw(ctx, "failed to read from source",
- "url", s.sourceURL, "error", err)
- return nil, fmt.Errorf("failed to read from %s: %w", s.sourceURL, err)
- }
- defer resp.Body.Close()
-
- var data sensorResponse
- if err := json.NewDecoder(resp.Body).Decode(&data); err != nil {
- return nil, fmt.Errorf("failed to decode response: %w", err)
- }
-
- return map[string]interface{}{
- "temperature": data.Temp,
- "humidity": data.Humidity,
- }, nil
-}
-```
-
-{{% /tab %}}
-{{< /tabs >}}
-
-{{< /expand >}}
-
-#### Understanding the entry point
-
-The generator also creates the entry point file that `viam-server` launches.
-You typically do not need to modify it.
-
-{{< tabs >}}
-{{% tab name="Python" %}}
-
-`src/main.py`:
-
-```python
-import asyncio
-from viam.module.module import Module
-from models.my_sensor import MySensor # noqa: F401
-
-
-if __name__ == "__main__":
- asyncio.run(Module.run_from_registry())
-```
-
-`run_from_registry()` automatically discovers all imported resource classes and
-registers them with `viam-server`. If you add more models to your module, import
-them here.
-
-{{% /tab %}}
-{{% tab name="Go" %}}
-
-`cmd/module/main.go`:
-
-```go
-package main
-
-import (
- mysensormodule "my-org/my-sensor-module"
- "go.viam.com/rdk/components/sensor"
- "go.viam.com/rdk/module"
- "go.viam.com/rdk/resource"
-)
-
-func main() {
- module.ModularMain(resource.APIModel{sensor.API, mysensormodule.Model})
-}
-```
-
-`ModularMain` handles socket parsing, signal handling, and graceful shutdown.
-The import of the resource package triggers its `init()` function, which calls
-`resource.RegisterComponent` to register the model. If you add more models,
-add more `resource.APIModel` entries to the `ModularMain` call.
-
-{{% /tab %}}
-{{< /tabs >}}
-
-### 3. Test locally
-
-Configure the module on your machine and verify it works.
-
-**Configure as a local module:**
-
-1. In the [Viam app](https://app.viam.com), navigate to your machine's
- **CONFIGURE** tab.
-2. Click **+**, select **Advanced**, then **Local module**.
-3. Set the **Executable path** to your module binary or script.
-4. Click **Create**.
-5. Click **+**, select **Advanced**, then **Local component**.
-6. Select your module, set the type and model, and configure attributes:
-
- ```json
- {
- "source_url": "https://api.example.com/sensor/data"
- }
- ```
-
-7. Click **Save**.
-
-**Test using the TEST card:**
-
-1. Find your sensor component and expand the **TEST** section.
-2. Your temperature and humidity values appear automatically under
- **GetReadings**.
-
-**Get a ready-to-run code sample:**
-
-The **CONNECT** tab on your machine's page in the Viam app provides generated
-code samples in Python and Go that connect to your machine and access all
-configured components. Use this as a starting point for application code that
-interacts with your module.
-
-**Rebuild and redeploy during development:**
-
-`viam-server` does not automatically detect changes to your module's source
-files or binary. After making changes, use the CLI to rebuild and redeploy:
-
-```bash
-# Build locally, transfer to machine, and restart the module
-viam module reload-local --part-id
-
-# Restart the module without rebuilding (e.g., after editing Python source)
-viam module restart --part-id
-```
-
-The `reload-local` command runs your build command from `meta.json`, transfers
-the artifact to the target machine, updates the machine config, and restarts
-the module. Use `--no-build` to skip the build step if you already built
-manually.
-
-### 4. Add logging
-
-Both the Python and Go SDKs provide a logger that writes to `viam-server`'s log
-stream, visible in the **LOGS** tab.
-
-{{< tabs >}}
-{{% tab name="Python" %}}
-
-```python
-self.logger.info("Sensor initialized with source URL: %s", self.source_url)
-self.logger.debug("Raw response from source: %s", data)
-self.logger.warning("Source returned unexpected field: %s", field_name)
-self.logger.error("Failed to connect to source: %s", error)
-```
-
-{{% /tab %}}
-{{% tab name="Go" %}}
-
-```go
-s.logger.CInfof(ctx, "Sensor initialized with source URL: %s", s.sourceURL)
-s.logger.CDebugf(ctx, "Raw response from source: %v", data)
-s.logger.CWarnw(ctx, "Source returned unexpected field", "field", fieldName)
-s.logger.CErrorw(ctx, "Failed to connect to source", "error", err)
-```
-
-{{% /tab %}}
-{{< /tabs >}}
-
-Use `info` for significant events, `debug` for detailed data, `warning` for
-recoverable problems, and `error` for failures.
-
-### 5. Handle dependencies
-
-Many modules need access to other resources on the same machine. To use
-another resource, you need to do three things:
-
-1. **Declare the dependency** in your config validation method by returning the
- resource name in the required (or optional) dependencies list.
-2. **Resolve the dependency** in your constructor or reconfigure method by
- looking it up from the `dependencies` map that `viam-server` passes in.
-3. **Call methods on it** in your API implementation, just like any other
- typed resource.
-
-The following example shows all three. It implements a sensor that depends on
-another sensor -- it reads Celsius temperature readings from the source sensor
-and converts them to Fahrenheit. Watch for the numbered comments in the code:
-
-{{< tabs >}}
-{{% tab name="Python" %}}
-
-```python
-class TempConverterSensor(Sensor, EasyResource):
- MODEL: ClassVar[Model] = Model(
- ModelFamily("my-org", "my-sensor-module"), "temp-converter"
- )
-
- source_sensor: Sensor
-
- @classmethod
- def validate_config(
- cls, config: ComponentConfig
- ) -> Tuple[Sequence[str], Sequence[str]]:
- fields = config.attributes.fields
- if "source_sensor" not in fields:
- raise Exception("source_sensor is required")
- source = fields["source_sensor"].string_value
- # 1. Declare: return the source sensor name as a required dependency
- return [source], []
-
- def reconfigure(self, config: ComponentConfig,
- dependencies: Mapping[ResourceName, ResourceBase]) -> None:
- source_name = config.attributes.fields["source_sensor"].string_value
- # 2. Resolve: find the dependency in the map viam-server passes in
- for name, dep in dependencies.items():
- if name.name == source_name:
- self.source_sensor = dep
- break
-
- async def get_readings(self, *, extra=None, timeout=None,
- **kwargs) -> Mapping[str, SensorReading]:
- # 3. Use: call methods on the dependency like any typed resource
- readings = await self.source_sensor.get_readings()
- celsius = readings["temperature"]
- return {"temperature_f": celsius * 9.0 / 5.0 + 32.0}
-```
-
-{{% /tab %}}
-{{% tab name="Go" %}}
-
-```go
-type ConverterConfig struct {
- SourceSensor string `json:"source_sensor"`
-}
-
-func (cfg *ConverterConfig) Validate(path string) ([]string, []string, error) {
- if cfg.SourceSensor == "" {
- return nil, nil, fmt.Errorf("source_sensor is required")
- }
- // 1. Declare: return the source sensor name as a required dependency
- return []string{cfg.SourceSensor}, nil, nil
-}
-
-type TempConverterSensor struct {
- resource.Named
- resource.AlwaysRebuild
- logger logging.Logger
- source sensor.Sensor
-}
-
-func newTempConverter(
- ctx context.Context,
- deps resource.Dependencies,
- conf resource.Config,
- logger logging.Logger,
-) (sensor.Sensor, error) {
- cfg, err := resource.NativeConfig[*ConverterConfig](conf)
- if err != nil {
- return nil, err
- }
-
- // 2. Resolve: look up the dependency by name from the map viam-server passes in
- src, err := sensor.FromProvider(deps, cfg.SourceSensor)
- if err != nil {
- return nil, fmt.Errorf("source sensor %q not found: %w",
- cfg.SourceSensor, err)
- }
-
- return &TempConverterSensor{
- Named: conf.ResourceName().AsNamed(),
- logger: logger,
- source: src,
- }, nil
-}
-
-func (s *TempConverterSensor) Readings(
- ctx context.Context,
- extra map[string]interface{},
-) (map[string]interface{}, error) {
- // 3. Use: call methods on the dependency like any typed resource
- readings, err := s.source.Readings(ctx, extra)
- if err != nil {
- return nil, fmt.Errorf("failed to read source sensor: %w", err)
- }
- celsius, ok := readings["temperature"].(float64)
- if !ok {
- return nil, fmt.Errorf("source sensor did not return a temperature reading")
- }
- return map[string]interface{}{
- "temperature_f": celsius*9.0/5.0 + 32.0,
- }, nil
-}
-```
-
-{{% /tab %}}
-{{< /tabs >}}
-
-### 6. Handle reconfiguration (optional)
-
-When a user changes a resource's configuration, `viam-server` calls your
-reconfiguration method instead of destroying and re-creating the resource.
-This is faster and preserves internal state like open connections.
-
-The generated code uses `resource.AlwaysRebuild` (Go) by default, which
-tells `viam-server` to destroy and re-create the resource on every config
-change. This is the simplest approach and works well for most modules.
-
-For in-place reconfiguration, implement the reconfiguration method to update
-your resource's fields directly:
-
-{{< tabs >}}
-{{% tab name="Python" %}}
-
-Implement `reconfigure` to update your resource from the new config. This
-method is also typically called from `new` (see the example in Step 2):
-
-```python
-def reconfigure(self, config: ComponentConfig,
- dependencies: Mapping[ResourceName, ResourceBase]) -> None:
- fields = config.attributes.fields
- self.source_url = fields["source_url"].string_value
- self.poll_interval = (
- fields["poll_interval"].number_value
- if "poll_interval" in fields
- else 10.0
- )
-```
-
-{{% /tab %}}
-{{% tab name="Go" %}}
-
-Remove `resource.AlwaysRebuild` from your struct and implement `Reconfigure`:
-
-```go
-type MySensor struct {
- resource.Named
- logger logging.Logger
- sourceURL string
- client *http.Client
-}
-
-func (s *MySensor) Reconfigure(ctx context.Context,
- deps resource.Dependencies, conf resource.Config) error {
- cfg, err := resource.NativeConfig[*Config](conf)
- if err != nil {
- return err
- }
- s.sourceURL = cfg.SourceURL
- s.client.Timeout = time.Duration(cfg.PollInterval) * time.Second
- return nil
-}
-```
-
-Go provides these helper traits as alternatives to writing a `Reconfigure`
-method. Embed one in your struct:
-
-| Trait | Behavior |
-| ---------------------------------- | ------------------------------------------------------------------------------------ |
-| `resource.AlwaysRebuild` | Resource is destroyed and re-created on every config change (the generated default). |
-| `resource.TriviallyReconfigurable` | Config changes are accepted silently with no action. |
-
-{{% /tab %}}
-{{< /tabs >}}
-
-### 7. Use the module data directory
-
-Every module gets a persistent data directory at the path specified by the
-`VIAM_MODULE_DATA` environment variable. Use this for caches, databases, or
-any state that should survive module restarts.
-
-{{< tabs >}}
-{{% tab name="Python" %}}
-
-```python
-import os
-
-data_dir = os.environ.get("VIAM_MODULE_DATA", "/tmp")
-cache_path = os.path.join(data_dir, "readings_cache.json")
-```
-
-{{% /tab %}}
-{{% tab name="Go" %}}
-
-```go
-dataDir := os.Getenv("VIAM_MODULE_DATA")
-cachePath := filepath.Join(dataDir, "readings_cache.json")
-```
-
-{{% /tab %}}
-{{< /tabs >}}
-
-The directory is created automatically by `viam-server` at
-`$VIAM_HOME/module-data///` (where `VIAM_HOME`
-defaults to `~/.viam`) and persists across module
-restarts and reconfigurations.
-
-## Try It
-
-1. Generate a sensor module using `viam module generate`.
-2. Open the generated resource file and implement your config validation and
- `GetReadings` method.
-3. Configure the module on your machine as a local module.
-4. Expand the **TEST** section on your sensor. Verify readings appear
- automatically under **GetReadings**.
-5. Enable data capture on the sensor. Wait one minute, then check the **DATA**
- tab to confirm readings are flowing to the cloud.
-6. Add a new key to the readings map (for example, `"pressure": 1013.25`).
- Rebuild and redeploy with `viam module reload-local`. Verify the new reading
- appears on the TEST card.
-
-## Troubleshooting
-
-{{< expand "Module crashes on startup" >}}
-
-- Check the **LOGS** tab for the crash traceback. The most common cause is a
- missing dependency -- a Python import not in `requirements.txt` or a Go
- package not in `go.mod`.
-- For Python, verify the module runs outside of `viam-server`:
- `python3 -m src.main` (from your module directory, with the virtualenv
- activated).
-- For Go, verify the binary runs: `./bin/` (the output path
- is set in your `Makefile`).
-- Check that your entrypoint script has execute permissions: `chmod +x run.sh`.
-
-{{< /expand >}}
-
-{{< expand "Module times out on startup" >}}
-
-`viam-server` expects the module to respond to a ready check within about 15
-seconds of launch. If your module does heavy initialization (loading large
-files, connecting to slow services), it may time out.
-
-- Move slow initialization out of `init()` or model registration and into the
- constructor instead, where it runs per-resource rather than blocking module
- startup.
-- Check the **LOGS** tab for timeout errors.
-
-{{< /expand >}}
-
-{{< expand "Dependency not found" >}}
-
-- Confirm the dependency name returned by your config validation method matches
- the resource name on the machine exactly (names are compared as strings, so
- case and spelling must match).
-- Verify the depended-on resource exists and is configured correctly.
-- Check for circular dependencies -- if A depends on B and B depends on A,
- both will fail to start. Check the **LOGS** tab for "circular dependency"
- errors.
-
-{{< /expand >}}
-
-{{< expand "Readings returning None or nil" >}}
-
-- Add logging inside your `GetReadings` implementation to see what data your
- source returns.
-- `GetReadings` must return a non-nil map. If it returns `nil` (Go) or `None`
- (Python), `viam-server` treats this as an error.
-- Check network connectivity from the machine if your sensor reads from an
- external source.
-
-{{< /expand >}}
-
-{{< expand "Module not restarting after code changes" >}}
-
-`viam-server` does not watch your module's source files or binary for changes.
-To deploy changes:
-
-- Use `viam module reload-local --part-id ` to rebuild and redeploy.
-- Use `viam module restart --part-id ` to restart without rebuilding.
-
-{{< /expand >}}
-
-{{< expand "Data capture not recording readings" >}}
-
-Data capture requires both the data management service and a per-resource
-capture configuration:
-
-- Verify the data management service is configured and does not have
- `capture_disabled` set to `true`.
-- Verify your sensor component has a data capture configuration with
- `capture_frequency_hz` greater than `0`.
-- Check that `GetReadings` returns a valid, non-nil map.
-- If the capture frequency is very low, you may need to wait longer to see
- data appear on the [**Data** page](https://app.viam.com/data).
-
-{{< /expand >}}
-
-## What's Next
-
-- [Write a Logic Module](/operate/modules/write-a-logic-module/) -- write a module
- that monitors sensors, coordinates components, or runs automation logic.
-- [Deploy a Module](/operate/modules/deploy-a-module/) -- package your module
- and upload it to the Viam registry for distribution.
diff --git a/docs/operate/modules/write-a-logic-module.md b/docs/operate/modules/write-a-logic-module.md
deleted file mode 100644
index 8dd805619d..0000000000
--- a/docs/operate/modules/write-a-logic-module.md
+++ /dev/null
@@ -1,726 +0,0 @@
----
-linkTitle: "Write a Logic Module"
-title: "Write a Control Logic Module"
-weight: 25
-layout: "docs"
-type: "docs"
-description: "Build a module that monitors sensors, coordinates components, or runs automation logic."
-date: "2025-03-06"
-aliases:
- - /development/write-a-logic-module/
- - /operate/modules/control-logic/
- - /manage/software/control-logic/
----
-
-Your machine has resources -- sensors, motors, cameras -- that work individually.
-A logic module makes them work together. It runs as a service alongside
-`viam-server`, declares dependencies on the resources it needs, and implements
-whatever control, monitoring, or coordination logic your application requires.
-
-Use a logic module when you need your machine to make decisions based on what
-it senses: trigger actions when readings cross a threshold, coordinate multiple
-components to accomplish a task, aggregate data from several sources, or run
-any continuous process that reads from some resources and acts on others.
-
-{{< alert title="Driver modules versus logic modules" color="tip" >}}
-A [driver module](/operate/modules/write-a-driver-module/) wraps hardware -- it implements
-a component API like sensor or motor so that `viam-server` can talk to a
-specific piece of hardware.
-
-A logic module (this page) orchestrates existing resources -- it reads from
-sensors, commands motors, and makes decisions. It typically implements a service
-API.
-
-Both are modules. The difference is what they do, not how they're built. The
-lifecycle, config validation, dependency, and deployment patterns are the same.
-{{< /alert >}}
-
-## Concepts
-
-### Module lifecycle
-
-Every module goes through a defined lifecycle:
-
-1. **Startup** -- `viam-server` launches the module as a separate process.
-2. **Validation** -- For each configured resource, `viam-server` calls a
- validation method that checks config attributes and declares dependencies
- on other resources.
-3. **Construction** -- `viam-server` calls the constructor with the validated
- config and resolved dependencies.
-4. **Reconfiguration** -- If the user changes the config, `viam-server` calls
- the validation method again, then the reconfiguration method.
-5. **Shutdown** -- `viam-server` calls the close method. Clean up resources
- (including background tasks) here.
-
-For more detail, see [Module Lifecycle](/operate/modules/lifecycle-of-a-module/).
-
-### Dependencies
-
-A logic module typically depends on other resources on the machine -- the
-sensors it monitors, the motors it controls. You declare dependencies in your
-validation method by returning their names. `viam-server` ensures those
-resources are ready before calling your constructor, and passes them in as a
-map you can look up by name.
-
-The pattern has three steps:
-
-1. **Declare** -- return dependency names from your validation method.
-2. **Resolve** -- look up each dependency from the map in your constructor.
-3. **Use** -- call methods on the resolved dependencies in your logic.
-
-The code examples below mark each step with numbered comments.
-
-### The generic service API
-
-The `generic` service API has a single method: `DoCommand`. It accepts an
-arbitrary key-value map and returns one. This makes it a flexible interface for
-custom logic -- you define your own command vocabulary.
-
-```json
-// Request
-{"command": "get_alerts", "severity": "critical"}
-
-// Response
-{"alerts": [{"sensor": "temp-1", "value": 42.5, "threshold": 40.0}]}
-```
-
-Use `generic` when your module's interface doesn't map to an existing service
-API (like `vision` or `mlmodel`). For the full list of service APIs, see
-[Resource APIs](/reference/apis/).
-
-### Background tasks
-
-Logic modules often need to run continuously -- polling sensors, checking
-thresholds, updating state. You can spawn background tasks in Go or C++. It is not recommended to spawn background tasks in Python because they will conflict with the Viam SDK's background tasks. If you need to run background logic in a Python module, instead place the logic in `DoCommand` and create a scheduled job to run the task on a schedule.
-
-The key requirement: your background task must stop cleanly when the module
-shuts down or reconfigures. Use a cancellation signal (a channel in Go) to coordinate this.
-
-## Steps
-
-When writing a logic module, follow the steps outlined below. To illustrate
-each step we'll use a temperature alert monitor as a worked example. It watches
-one or more sensors, compares their readings against configurable thresholds,
-and maintains a list of active alerts that your application code can query.
-
-### 1. Generate a generic service module
-
-```bash
-viam module generate
-```
-
-| Prompt | What to enter | Why |
-| ---------------- | --------------------------- | -------------------------------- |
-| Module name | `alert-monitor` | A short, descriptive name |
-| Language | `python` or `go` | Your implementation language |
-| Visibility | `private` | Keep it private while developing |
-| Namespace | Your organization namespace | Scopes the module to your org |
-| Resource subtype | `generic` (under services) | Flexible service API |
-| Model name | `temp-alert` | The model name for your service |
-| Register | `yes` | Registers the module with Viam |
-
-The generator creates a complete project. The key files you will edit:
-
-{{< tabs >}}
-{{% tab name="Python" %}}
-
-| File | Purpose |
-| -------------------------- | ----------------------------------------------------------- |
-| `src/models/temp_alert.py` | Service class skeleton -- you will edit this |
-| `src/main.py` | Entry point -- starts the module server (no changes needed) |
-| `meta.json` | Module metadata for the registry |
-
-{{% /tab %}}
-{{% tab name="Go" %}}
-
-| File | Purpose |
-| -------------------- | ----------------------------------------------------------- |
-| `alert_monitor.go` | Service implementation skeleton -- you will edit this |
-| `cmd/module/main.go` | Entry point -- starts the module server (no changes needed) |
-| `meta.json` | Module metadata for the registry |
-
-{{% /tab %}}
-{{< /tabs >}}
-
-### 2. Define the config
-
-Open the generated resource file. Define config attributes for the sensors to
-monitor and the alert thresholds.
-
-{{< tabs >}}
-{{% tab name="Python" %}}
-
-In `src/models/temp_alert.py`, add config attributes to your class:
-
-```python
-class TempAlert(Generic, EasyResource):
- MODEL: ClassVar[Model] = Model(
- ModelFamily("my-org", "alert-monitor"), "temp-alert"
- )
-
- sensor_names: list[str]
- max_temp: float
- poll_interval: float
- alerts: list[dict]
- _monitor_task: Optional[asyncio.Task]
- _stop_event: asyncio.Event
-```
-
-{{% /tab %}}
-{{% tab name="Go" %}}
-
-In the generated `.go` file, add fields to the `Config` struct. Each field
-needs a `json` tag matching the attribute name users set in their config JSON.
-
-Then update the `Validate` method. It returns three values: a list of required
-dependency names, a list of optional dependency names, and an error.
-
-```go
-type Config struct {
- SensorNames []string `json:"sensor_names"`
- MaxTemp float64 `json:"max_temp"`
- PollInterval float64 `json:"poll_interval_secs"`
-}
-
-func (cfg *Config) Validate(path string) ([]string, []string, error) {
- if len(cfg.SensorNames) == 0 {
- return nil, nil, fmt.Errorf("sensor_names is required")
- }
- if cfg.MaxTemp == 0 {
- return nil, nil, fmt.Errorf("max_temp is required")
- }
- // 1. Declare: return all sensor names as required dependencies
- return cfg.SensorNames, nil, nil
-}
-```
-
-{{% /tab %}}
-{{< /tabs >}}
-
-### 3. Implement the constructor
-
-The constructor receives the validated config and a `dependencies` map
-containing the resources you declared in the validation method. Look up each
-dependency by name, store it on your struct/instance, and start the background
-monitoring loop.
-
-{{< tabs >}}
-{{% tab name="Python" %}}
-
-Update `validate_config`, `new`, and `reconfigure`:
-
-```python
- @classmethod
- def validate_config(
- cls, config: ComponentConfig
- ) -> Tuple[Sequence[str], Sequence[str]]:
- fields = config.attributes.fields
- if "sensor_names" not in fields:
- raise Exception("sensor_names is required")
- if "max_temp" not in fields:
- raise Exception("max_temp is required")
- sensor_names = [
- v.string_value
- for v in fields["sensor_names"].list_value.values
- ]
- # 1. Declare: return sensor names as required dependencies
- return sensor_names, []
-
- @classmethod
- def new(cls, config: ComponentConfig,
- dependencies: Mapping[ResourceName, ResourceBase]) -> Self:
- instance = cls(config.name)
- instance.alerts = []
- instance._monitor_task = None
- instance._stop_event = asyncio.Event()
- instance.reconfigure(config, dependencies)
- return instance
-
- def reconfigure(self, config: ComponentConfig,
- dependencies: Mapping[ResourceName, ResourceBase]) -> None:
- # Stop any existing monitor loop
- if self._monitor_task is not None:
- self._stop_event.set()
- self._monitor_task = None
-
- fields = config.attributes.fields
- self.sensor_names = [
- v.string_value
- for v in fields["sensor_names"].list_value.values
- ]
- self.max_temp = fields["max_temp"].number_value
- self.poll_interval = (
- fields["poll_interval_secs"].number_value
- if "poll_interval_secs" in fields
- else 10.0
- )
-
- # 2. Resolve: find each sensor in the dependencies map
- self.sensors = {}
- for name, dep in dependencies.items():
- if name.name in self.sensor_names:
- self.sensors[name.name] = dep
-
- # Start the monitor loop
- self._stop_event = asyncio.Event()
- self._monitor_task = asyncio.create_task(self._monitor_loop())
-```
-
-{{% /tab %}}
-{{% tab name="Go" %}}
-
-Update the struct and constructor. `resource.Named` provides the `Name()`
-method that `viam-server` requires. `resource.NativeConfig` converts the raw
-config into your typed struct. `sensor.FromProvider` looks up a sensor
-dependency by name from the dependencies map.
-
-```go
-type TempAlert struct {
- resource.Named
- logger logging.Logger
- cfg *Config
- sensors map[string]sensor.Sensor
- mu sync.Mutex
- alerts []Alert
- cancelFn func()
-}
-
-type Alert struct {
- Sensor string `json:"sensor"`
- Value float64 `json:"value"`
- Threshold float64 `json:"threshold"`
- Time string `json:"time"`
-}
-
-func newTempAlert(
- ctx context.Context,
- deps resource.Dependencies,
- conf resource.Config,
- logger logging.Logger,
-) (resource.Resource, error) {
- cfg, err := resource.NativeConfig[*Config](conf)
- if err != nil {
- return nil, err
- }
-
- // 2. Resolve: find each sensor in the dependencies map
- sensors := make(map[string]sensor.Sensor)
- for _, name := range cfg.SensorNames {
- s, err := sensor.FromProvider(deps, name)
- if err != nil {
- return nil, fmt.Errorf("sensor %q not found: %w", name, err)
- }
- sensors[name] = s
- }
-
- monitorCtx, cancelFn := context.WithCancel(context.Background())
- svc := &TempAlert{
- Named: conf.ResourceName().AsNamed(),
- logger: logger,
- cfg: cfg,
- sensors: sensors,
- alerts: []Alert{},
- cancelFn: cancelFn,
- }
-
- // Start background monitor loop
- go svc.monitorLoop(monitorCtx)
-
- return svc, nil
-}
-```
-
-{{% /tab %}}
-{{< /tabs >}}
-
-### 4. Implement the background loop
-
-The monitor loop polls sensors at a fixed interval and checks readings against
-thresholds. When a reading exceeds the threshold, it creates an alert.
-
-{{< tabs >}}
-{{% tab name="Python" %}}
-
-```python
- async def _monitor_loop(self):
- while not self._stop_event.is_set():
- for name, s in self.sensors.items():
- try:
- # 3. Use: call methods on dependencies
- readings = await s.get_readings()
- temp = readings.get("temperature")
- if temp is not None and temp > self.max_temp:
- alert = {
- "sensor": name,
- "value": temp,
- "threshold": self.max_temp,
- "time": datetime.now().isoformat(),
- }
- self.alerts.append(alert)
- self.logger.warning(
- "Alert: %s reported %.1f (threshold: %.1f)",
- name, temp, self.max_temp,
- )
- except Exception as e:
- self.logger.error("Failed to read %s: %s", name, e)
-
- try:
- await asyncio.wait_for(
- self._stop_event.wait(),
- timeout=self.poll_interval,
- )
- break # Stop event was set
- except asyncio.TimeoutError:
- pass # Continue polling
-```
-
-{{% /tab %}}
-{{% tab name="Go" %}}
-
-```go
-func (s *TempAlert) monitorLoop(ctx context.Context) {
- interval := time.Duration(s.cfg.PollInterval) * time.Second
- if interval == 0 {
- interval = 10 * time.Second
- }
-
- ticker := time.NewTicker(interval)
- defer ticker.Stop()
-
- for {
- select {
- case <-ctx.Done():
- return
- case <-ticker.C:
- s.checkSensors(ctx)
- }
- }
-}
-
-func (s *TempAlert) checkSensors(ctx context.Context) {
- s.mu.Lock()
- defer s.mu.Unlock()
-
- for name, sens := range s.sensors {
- // 3. Use: call methods on dependencies
- readings, err := sens.Readings(ctx, nil)
- if err != nil {
- s.logger.CErrorw(ctx, "failed to read sensor", "sensor", name, "error", err)
- continue
- }
- temp, ok := readings["temperature"].(float64)
- if !ok {
- continue
- }
- if temp > s.cfg.MaxTemp {
- alert := Alert{
- Sensor: name,
- Value: temp,
- Threshold: s.cfg.MaxTemp,
- Time: time.Now().Format(time.RFC3339),
- }
- s.alerts = append(s.alerts, alert)
- s.logger.CWarnw(ctx, "alert triggered",
- "sensor", name, "value", temp, "threshold", s.cfg.MaxTemp)
- }
- }
-}
-```
-
-{{% /tab %}}
-{{< /tabs >}}
-
-{{< alert title="Note: lock scope" color="note" >}}
-The Go example holds the mutex for the entire `checkSensors` call, including sensor reads that may block on network IO.
-This keeps the code simple and familiar (`defer s.mu.Unlock()`), but it means `DoCommand` calls will block while sensors are being read.
-
-In production, you would copy `s.sensors` under the lock, release it, read sensors without the lock, then re-lock to append alerts.
-{{< /alert >}}
-
-### 5. Implement DoCommand
-
-`DoCommand` is the interface your application code uses to interact with the
-service. Define a command vocabulary that makes sense for your module.
-
-{{< tabs >}}
-{{% tab name="Python" %}}
-
-```python
- async def do_command(
- self,
- command: Mapping[str, ValueTypes],
- *,
- timeout: Optional[float] = None,
- **kwargs,
- ) -> Mapping[str, ValueTypes]:
- cmd = command.get("command", "")
-
- if cmd == "get_alerts":
- return {"alerts": self.alerts}
-
- if cmd == "get_alert_count":
- return {"count": len(self.alerts)}
-
- if cmd == "acknowledge":
- self.alerts.clear()
- return {"status": "ok"}
-
- if cmd == "set_threshold":
- self.max_temp = command["max_temp"]
- return {"status": "ok", "max_temp": self.max_temp}
-
- raise Exception(f"Unknown command: {cmd}")
-```
-
-{{% /tab %}}
-{{% tab name="Go" %}}
-
-```go
-func (s *TempAlert) DoCommand(
- ctx context.Context,
- cmd map[string]interface{},
-) (map[string]interface{}, error) {
- command, _ := cmd["command"].(string)
-
- switch command {
- case "get_alerts":
- s.mu.Lock()
- defer s.mu.Unlock()
- // Convert alerts to interface slice for serialization
- alertList := make([]interface{}, len(s.alerts))
- for i, a := range s.alerts {
- alertList[i] = map[string]interface{}{
- "sensor": a.Sensor,
- "value": a.Value,
- "threshold": a.Threshold,
- "time": a.Time,
- }
- }
- return map[string]interface{}{"alerts": alertList}, nil
-
- case "get_alert_count":
- s.mu.Lock()
- defer s.mu.Unlock()
- return map[string]interface{}{"count": len(s.alerts)}, nil
-
- case "acknowledge":
- s.mu.Lock()
- defer s.mu.Unlock()
- s.alerts = s.alerts[:0]
- return map[string]interface{}{"status": "ok"}, nil
-
- case "set_threshold":
- newMax, ok := cmd["max_temp"].(float64)
- if !ok {
- return nil, fmt.Errorf("max_temp must be a number")
- }
- s.mu.Lock()
- s.cfg.MaxTemp = newMax
- s.mu.Unlock()
- return map[string]interface{}{"status": "ok", "max_temp": newMax}, nil
-
- default:
- return nil, fmt.Errorf("unknown command: %s", command)
- }
-}
-```
-
-{{% /tab %}}
-{{< /tabs >}}
-
-### 6. Handle shutdown
-
-When `viam-server` stops the module or reconfigures it, your background loop
-must stop cleanly. Without this, goroutines or async tasks leak.
-
-{{< tabs >}}
-{{% tab name="Python" %}}
-
-```python
- async def close(self):
- self._stop_event.set()
- if self._monitor_task is not None:
- await self._monitor_task
- self._monitor_task = None
- self.logger.info("TempAlert monitor stopped")
-```
-
-{{% /tab %}}
-{{% tab name="Go" %}}
-
-```go
-func (s *TempAlert) Close(ctx context.Context) error {
- s.cancelFn()
- s.logger.CInfof(ctx, "TempAlert monitor stopped")
- return nil
-}
-```
-
-In Go, the `Reconfigure` method should also stop the old loop and start a new
-one:
-
-```go
-func (s *TempAlert) Reconfigure(
- ctx context.Context,
- deps resource.Dependencies,
- conf resource.Config,
-) error {
- // Stop the old loop
- s.cancelFn()
-
- cfg, err := resource.NativeConfig[*Config](conf)
- if err != nil {
- return err
- }
-
- sensors := make(map[string]sensor.Sensor)
- for _, name := range cfg.SensorNames {
- sens, err := sensor.FromProvider(deps, name)
- if err != nil {
- return fmt.Errorf("sensor %q not found: %w", name, err)
- }
- sensors[name] = sens
- }
-
- monitorCtx, cancelFn := context.WithCancel(context.Background())
-
- s.mu.Lock()
- s.cfg = cfg
- s.sensors = sensors
- s.cancelFn = cancelFn
- s.mu.Unlock()
-
- go s.monitorLoop(monitorCtx)
- return nil
-}
-```
-
-{{% /tab %}}
-{{< /tabs >}}
-
-### 7. Test locally
-
-**Configure the service on your machine:**
-
-1. In the [Viam app](https://app.viam.com), navigate to your machine's
- **CONFIGURE** tab.
-2. Ensure you have at least one sensor configured (this is the resource your
- logic module will monitor).
-3. Click **+**, select **Advanced**, then **Local module**.
-4. Set the **Executable path** to your module binary or script.
-5. Click **Create**.
-6. Click **+**, select **Advanced**, then **Local service**.
-7. Select your module, set the type to `generic` and the model to your model
- name, and configure attributes:
-
- ```json
- {
- "sensor_names": ["my-temp-sensor"],
- "max_temp": 30.0,
- "poll_interval_secs": 5
- }
- ```
-
-8. Click **Save**.
-
-**Test with DoCommand:**
-
-On the **CONFIGURE** tab, expand your service's card and find the **DoCommand**
-section. Send a command:
-
-```json
-{ "command": "get_alerts" }
-```
-
-You should see a response with any alerts that have been triggered.
-
-**Get a ready-to-run code sample:**
-
-The **CONNECT** tab on your machine's page in the Viam app provides generated
-code samples in Python and Go that connect to your machine and access all
-configured resources. Use this as a starting point for application code that
-sends `DoCommand` requests to your service.
-
-**Rebuild and redeploy during development:**
-
-`viam-server` does not automatically detect changes to your module's source
-files or binary. After making changes, use the CLI to rebuild and redeploy:
-
-```bash
-# Build locally, transfer to machine, and restart the module
-viam module reload-local --part-id
-
-# Restart the module without rebuilding (e.g., after editing Python source)
-viam module restart --part-id
-```
-
-**Test the alert flow:**
-
-1. Verify your sensor is returning temperature readings on the TEST card.
-2. Set `max_temp` to a value below the current temperature so alerts trigger.
-3. Wait for one poll interval, then send `{"command": "get_alerts"}`.
-4. You should see alerts in the response.
-5. Send `{"command": "acknowledge"}` to clear them.
-
-## Try It
-
-1. Generate a generic service module using `viam module generate`.
-2. Define config attributes for monitored sensors and thresholds.
-3. Implement the constructor to resolve sensor dependencies and start the
- monitor loop.
-4. Implement `DoCommand` with `get_alerts`, `acknowledge`, and `set_threshold`
- commands.
-5. Configure the module on your machine with a real sensor.
-6. Lower the threshold below the current temperature and verify alerts appear.
-
-## Troubleshooting
-
-{{< expand "Background loop not running" >}}
-
-- Check the **LOGS** tab for errors from the monitor loop. A failing sensor
- read can cause the loop to exit silently.
-- Verify the `poll_interval_secs` is greater than 0.
-- In Python, ensure you are creating an `asyncio.Task` (not just calling the
- async function without `await` or `create_task`).
-- In Go, ensure the goroutine context is not prematurely canceled. Use
- `context.Background()` for the loop context, not the request context.
-
-{{< /expand >}}
-
-{{< expand "DoCommand returning unexpected results" >}}
-
-- Verify the `command` field in your request matches the command names in your
- implementation exactly (case-sensitive).
-- Check that value types match. JSON numbers are `float64` in Go and `float`
- in Python. If you send `"max_temp": 30`, Go receives it as `float64(30)`.
-- Add logging inside `DoCommand` to see the raw command map.
-
-{{< /expand >}}
-
-{{< expand "Sensor dependency not available" >}}
-
-- Confirm the sensor names in `sensor_names` match the names of sensors
- configured on the machine exactly.
-- Verify the sensors are configured and working before the logic module starts.
- Check the **CONTROL** tab to confirm each sensor returns readings.
-- If a sensor is added after the logic module is already running, reconfigure
- the logic module (re-save its config) so `viam-server` re-resolves
- dependencies.
-
-{{< /expand >}}
-
-{{< expand "Alerts not appearing" >}}
-
-- Check that your sensor's readings map includes a `"temperature"` key. The
- monitor loop checks for this specific key.
-- Verify `max_temp` is set below the actual temperature so alerts trigger.
-- Check the **LOGS** tab for warning messages from the monitor loop.
-
-{{< /expand >}}
-
-## What's Next
-
-- [Write a Driver Module](/operate/modules/write-a-driver-module/) -- write a module
- that wraps custom hardware.
-- [Deploy a Module](/operate/modules/deploy-a-module/) -- package and upload your
- module to the Viam registry.
diff --git a/docs/operate/modules/write-an-inline-module.md b/docs/operate/modules/write-an-inline-module.md
deleted file mode 100644
index 25f6570090..0000000000
--- a/docs/operate/modules/write-an-inline-module.md
+++ /dev/null
@@ -1,570 +0,0 @@
----
-linkTitle: "Write an Inline Module"
-title: "Write an Inline Module"
-weight: 10
-layout: "docs"
-type: "docs"
-description: "Write and deploy a custom module directly in the browser using Viam's inline module editor."
-date: "2025-01-30"
-aliases:
- - /build/development/write-an-inline-module/
- - /development/write-an-inline-module/
----
-
-Viam provides built-in support for many types of hardware and software, but you
-may want to use hardware that Viam doesn't support out of the box, or add
-application-specific logic. Modules let you add that support yourself.
-
-An inline module is the fastest way to get started. You write your module code
-directly in the Viam app's browser-based editor -- no IDE, terminal, or GitHub
-account required. When you click **Save & Deploy**, Viam builds your module in
-the cloud and deploys it to your machine automatically.
-
-## Concepts
-
-### What is an inline module?
-
-An inline module is a Viam-hosted module. Viam manages the source code,
-versioning, builds, and deployment for you. You edit a single source file in the
-browser, and Viam handles everything else:
-
-- **Source code** is stored and versioned by Viam (not in a git repository).
-- **Builds** run automatically in the cloud when you save.
-- **Deployment** happens automatically -- machines configured with your module
- receive the new version within minutes.
-
-### Inline modules versus externally managed modules
-
-| | Inline (Viam-hosted) | Externally managed |
-| ------------------------ | ------------------------------------------------------ | ------------------------------------------------------------- |
-| **Where you write code** | Browser editor in the Viam app | Your own IDE, locally or in a repo |
-| **Source control** | Managed by Viam | Your own git repository |
-| **Build system** | Automatic cloud builds on save | CLI upload or GitHub Actions |
-| **Versioning** | Automatic (`0.0.1`, `0.0.2`, ...) | You choose semantic versions |
-| **Visibility** | Private to your organization | Private or public |
-| **Best for** | Prototyping, simple control logic, no-toolchain setups | Production modules, public distribution, complex dependencies |
-
-Both types run identically at runtime -- as child processes communicating with
-`viam-server` over gRPC. The difference is how you create, edit, and deploy the
-module.
-
-If you want to manage your own source code and build pipeline, see
-[Write a Driver Module](/operate/modules/write-a-driver-module/) or
-[Write a Logic Module](/operate/modules/write-a-logic-module/) instead.
-
-### The Generic service
-
-Inline modules create a Generic **service** (not a component). The Generic
-service provides a single method -- `DoCommand` -- that accepts an arbitrary
-JSON command and returns a JSON response. Your control logic goes inside
-`DoCommand`.
-
-Use the Generic service when:
-
-- You want to coordinate multiple components (read a sensor, move a servo).
-- You need a simple command-response interface.
-- You are prototyping and want to iterate quickly.
-
-## Steps
-
-### 1. Create the module
-
-1. In the [Viam app](https://app.viam.com), navigate to your machine's
- **CONFIGURE** tab.
-2. Click **+** and select **Control code**.
-3. In the "Choose where to host your code" dialog, select **Viam-hosted** and
- click **Choose**.
-4. Name your module (for example, `servo-distance-control`) and choose a language
- (**Python** or **Go**).
-5. Click **Create module**.
-
-The browser opens the code editor with a working template that includes all
-necessary imports and method stubs.
-
-### 2. Understand the template
-
-The editor opens a single file -- your module's main source file. The template
-includes three methods you need to fill in:
-
-{{< tabs >}}
-{{% tab name="Python" %}}
-
-The editable file is `src/models/generic_service.py`. It contains a class that
-extends `GenericService` and `EasyResource`:
-
-```python
-class MyGenericService(GenericService, EasyResource):
- MODEL: ClassVar[Model] = Model(
- ModelFamily("my-org", "my-module"), "generic-service"
- )
-```
-
-The three methods to implement:
-
-- **`validate_config`** -- check that configuration attributes are valid and
- declare dependencies.
-- **`new`** -- initialize your service with attributes and dependencies.
-- **`do_command`** -- your control logic.
-
-{{< alert title="Important" color="caution" >}}
-Do not change the class name or the `MODEL` triplet. Viam uses these
-auto-generated values to identify your module. Changing them will break your
-inline module.
-{{< /alert >}}
-
-{{% /tab %}}
-{{% tab name="Go" %}}
-
-The editable file is `module.go`. It contains a struct, a config type, and
-registration logic:
-
-```go
-var GenericService = resource.NewModel(
- "my-org", "my-module", "generic-service",
-)
-
-func init() {
- resource.RegisterService(genericservice.API, GenericService,
- resource.Registration[resource.Resource, *Config]{
- Constructor: newGenericService,
- },
- )
-}
-```
-
-The three areas to implement:
-
-- **`Validate`** on the `Config` struct -- check attributes, return
- dependencies.
-- **`NewGenericService`** -- initialize the service with attributes and
- dependencies.
-- **`DoCommand`** -- your control logic.
-
-{{< alert title="Important" color="caution" >}}
-Do not change the model name triplet, struct names, or public function names.
-Viam uses these auto-generated values to identify your module. Changing them
-will break your inline module.
-{{< /alert >}}
-
-{{% /tab %}}
-{{< /tabs >}}
-
-### 3. Implement validate_config
-
-The validate method runs every time the machine configuration changes. It checks
-that the attributes passed to your service are valid and declares dependencies
-on other components or services.
-
-This example validates attributes for a distance-responsive servo controller --
-a service that reads an ultrasonic sensor and adjusts a servo angle based on
-distance:
-
-{{< tabs >}}
-{{% tab name="Python" %}}
-
-```python
-@classmethod
-def validate_config(
- cls, config: ComponentConfig
-) -> Tuple[Sequence[str], Sequence[str]]:
- attrs = struct_to_dict(config.attributes)
-
- # Required numeric attributes
- sensor_range_start = attrs.get("sensor_range_start")
- if sensor_range_start is None or not isinstance(
- sensor_range_start, (int, float)
- ):
- raise ValueError(
- "attribute 'sensor_range_start' is required "
- "and must be an int or float value"
- )
-
- sensor_range_end = attrs.get("sensor_range_end")
- if sensor_range_end is None or not isinstance(
- sensor_range_end, (int, float)
- ):
- raise ValueError(
- "attribute 'sensor_range_end' is required "
- "and must be an int or float value"
- )
-
- # Required dependency attributes
- required_deps: List[str] = []
-
- servo_name = attrs.get("servo")
- if not isinstance(servo_name, str) or not servo_name:
- raise ValueError(
- "attribute 'servo' (non-empty string) is required"
- )
- required_deps.append(servo_name)
-
- sensor_name = attrs.get("sensor")
- if not isinstance(sensor_name, str) or not sensor_name:
- raise ValueError(
- "attribute 'sensor' (non-empty string) is required"
- )
- required_deps.append(sensor_name)
-
- return required_deps, []
-```
-
-The return value is a tuple of two lists:
-
-1. **Required dependencies** -- component or service names that must exist and
- be ready before your service starts.
-2. **Optional dependencies** -- names your service can use if available but
- does not require.
-
-{{% /tab %}}
-{{% tab name="Go" %}}
-
-```go
-type Config struct {
- SensorRangeStart float64 `json:"sensor_range_start"`
- SensorRangeEnd float64 `json:"sensor_range_end"`
- ServoAngleMin *int64 `json:"servo_angle_min"`
- ServoAngleMax *int64 `json:"servo_angle_max"`
- Reversed *bool `json:"reversed"`
- Servo string `json:"servo"`
- Sensor string `json:"sensor"`
-}
-
-func (cfg *Config) Validate(path string) ([]string, []string, error) {
- if cfg.SensorRangeStart == 0 {
- return nil, nil, fmt.Errorf(
- "%s: 'sensor_range_start' is required and must be non-zero",
- path,
- )
- }
- if cfg.SensorRangeEnd == 0 {
- return nil, nil, fmt.Errorf(
- "%s: 'sensor_range_end' is required and must be non-zero",
- path,
- )
- }
-
- requiredDeps := []string{}
- if cfg.Servo == "" {
- return nil, nil, fmt.Errorf(
- "%s: 'servo' (non-empty string) is required", path,
- )
- }
- requiredDeps = append(requiredDeps, cfg.Servo)
-
- if cfg.Sensor == "" {
- return nil, nil, fmt.Errorf(
- "%s: 'sensor' (non-empty string) is required", path,
- )
- }
- requiredDeps = append(requiredDeps, cfg.Sensor)
-
- return requiredDeps, []string{}, nil
-}
-```
-
-The `Validate` method returns two slices (required dependencies and optional
-dependencies) and an error.
-
-{{% /tab %}}
-{{< /tabs >}}
-
-### 4. Implement the constructor
-
-The constructor runs when the service is first created and again whenever its
-configuration changes. Use it to parse attributes and resolve dependencies.
-
-{{< tabs >}}
-{{% tab name="Python" %}}
-
-```python
-@classmethod
-def new(
- cls, config: ComponentConfig,
- dependencies: Mapping[ResourceName, ResourceBase]
-) -> Self:
- attrs = struct_to_dict(config.attributes)
- self = cls(config.name)
-
- # Required attributes
- self.sensor_range_start = float(attrs.get("sensor_range_start"))
- self.sensor_range_end = float(attrs.get("sensor_range_end"))
-
- # Optional attributes with defaults
- self.servo_angle_min = float(attrs.get("servo_angle_min", 0))
- self.servo_angle_max = float(attrs.get("servo_angle_max", 180))
- self.reversed = attrs.get("reversed", False)
-
- # Resolve dependencies
- servo_name = attrs.get("servo")
- sensor_name = attrs.get("sensor")
- self.servo = dependencies[Servo.get_resource_name(servo_name)]
- self.sensor = dependencies[Sensor.get_resource_name(sensor_name)]
-
- return self
-```
-
-{{% /tab %}}
-{{% tab name="Go" %}}
-
-```go
-func NewGenericService(
- ctx context.Context, deps resource.Dependencies,
- name resource.Name, conf *Config, logger logging.Logger,
-) (resource.Resource, error) {
- cancelCtx, cancelFunc := context.WithCancel(context.Background())
-
- // Apply defaults for optional fields
- servoAngleMin := int64(0)
- if conf.ServoAngleMin != nil {
- servoAngleMin = *conf.ServoAngleMin
- }
- servoAngleMax := int64(180)
- if conf.ServoAngleMax != nil {
- servoAngleMax = *conf.ServoAngleMax
- }
- reversed := false
- if conf.Reversed != nil {
- reversed = *conf.Reversed
- }
-
- // Resolve dependencies
- servoDep, err := servo.FromProvider(deps, conf.Servo)
- if err != nil {
- return nil, err
- }
- sensorDep, err := sensor.FromProvider(deps, conf.Sensor)
- if err != nil {
- return nil, err
- }
-
- return &genericService{
- name: name,
- logger: logger,
- cfg: conf,
- cancelCtx: cancelCtx,
- cancelFunc: cancelFunc,
- servo: servoDep,
- sensor: sensorDep,
- sensorRangeStart: conf.SensorRangeStart,
- sensorRangeEnd: conf.SensorRangeEnd,
- servoAngleMin: servoAngleMin,
- servoAngleMax: servoAngleMax,
- reversed: reversed,
- }, nil
-}
-```
-
-{{% /tab %}}
-{{< /tabs >}}
-
-### 5. Implement DoCommand
-
-`DoCommand` is where your control logic goes. This example reads a distance
-sensor and maps the reading to a servo angle:
-
-{{< tabs >}}
-{{% tab name="Python" %}}
-
-```python
-async def do_command(
- self, command: Mapping[str, ValueTypes], *,
- timeout: Optional[float] = None, **kwargs
-) -> Mapping[str, ValueTypes]:
- readings = await self.sensor.get_readings()
- if not readings:
- raise ValueError("No sensor readings available")
-
- value = next(iter(readings.values()))
-
- # Map sensor range to servo angle range
- t = (value - self.sensor_range_start) / (
- self.sensor_range_end - self.sensor_range_start
- )
- t = max(0.0, min(1.0, 1.0 - t if self.reversed else t))
-
- angle = self.servo_angle_min + t * (
- self.servo_angle_max - self.servo_angle_min
- )
- await self.servo.move(int(angle))
-
- return {"servo_angle_deg": angle}
-```
-
-{{% /tab %}}
-{{% tab name="Go" %}}
-
-```go
-func (s *genericService) DoCommand(
- ctx context.Context, cmd map[string]interface{},
-) (map[string]interface{}, error) {
- readings, err := s.sensor.Readings(ctx, nil)
- if err != nil {
- return nil, err
- }
- value, ok := readings["distance"].(float64)
- if !ok {
- return nil, fmt.Errorf("sensor reading 'distance' must be a float64")
- }
-
- // Map sensor range to servo angle range
- t := (value - s.sensorRangeStart) /
- (s.sensorRangeEnd - s.sensorRangeStart)
- if t < 0 { t = 0 } else if t > 1 { t = 1 }
- if s.reversed { t = 1 - t }
-
- angle := float64(s.servoAngleMin) +
- t * (float64(s.servoAngleMax) - float64(s.servoAngleMin))
- return map[string]interface{}{
- "servo_angle_deg": angle,
- }, s.servo.Move(ctx, uint32(angle), nil)
-}
-```
-
-{{% /tab %}}
-{{< /tabs >}}
-
-In this example no DoCommand payload is used. You can use the command payload to
-customize behavior per invocation. Attributes are constant across all
-invocations; the DoCommand payload can vary with each call.
-
-### 6. Save and deploy
-
-1. Click **Save & Deploy** in the code editor toolbar.
-2. Viam uploads your code as a new version and starts a cloud build.
-3. Builds typically take 2-5 minutes. You can continue editing while a build
- runs -- your next save creates a new version.
-4. If the build fails, click **View Logs** to see what went wrong.
-
-Each save creates a new version in your module's history. You can switch between
-versions using the version dropdown in the editor toolbar.
-
-### 7. Test on a machine
-
-#### Add the module to a machine
-
-1. In the code editor, click **Add to machine**.
-2. Select a location, machine, and part.
-3. Click **Add**.
-
-The Viam app navigates you to the machine's **CONFIGURE** tab with your module
-added.
-
-#### Configure the service
-
-1. In the module section, click **Add** to add a model of your generic service.
-2. Click **+** to add each dependency your service requires (for example, a servo and
- a sensor). Configure each dependency with the appropriate attributes.
-3. Configure the attributes for your generic service:
-
-```json
-{
- "sensor_range_start": 0.05,
- "sensor_range_end": 0.3,
- "servo_angle_min": 40,
- "servo_angle_max": 270,
- "reversed": true,
- "servo": "servo-1",
- "sensor": "sensor-1"
-}
-```
-
-4. Click **Save**.
-
-#### Send test commands
-
-1. On the **CONFIGURE** tab, expand your generic service's card.
-2. Find the **DoCommand** section.
-3. Enter a command (or an empty map `{}` if your DoCommand does not use the
- payload):
-
-```json
-{}
-```
-
-4. Click **Execute**. You should see a response like:
-
-```json
-{ "servo_angle_deg": 155.0 }
-```
-
-### 8. Automate with a scheduled job
-
-The DoCommand section in the Viam app runs your logic once per click. To have
-it run automatically:
-
-1. Click **+** and select **Job**.
-2. Name the job and click **Create**.
-3. Choose a schedule. For control logic that should always run, select
- **Continuous**.
-4. Select your generic service as the resource.
-5. Edit the DoCommand payload or leave it as an empty map if no payload is
- needed.
-6. Click **Save**.
-
-## Try It
-
-1. Create a new inline module from the **+** menu.
-2. Implement `validate_config`, the constructor, and `do_command`.
-3. Click **Save & Deploy** and wait for the build to complete.
-4. Add the module to a machine and configure the service with dependencies.
-5. Execute a DoCommand and verify the response.
-6. Set up a scheduled job to run the logic continuously.
-
-## Troubleshooting
-
-{{< expand "\"Viam-hosted\" option not visible" >}}
-
-The inline module feature is gated by a feature flag. If you do not see the
-"Viam-hosted" option when clicking **+** → **Control code**, your organization
-may not have the feature enabled. Contact Viam support to request access.
-
-{{< /expand >}}
-
-{{< expand "Build fails after saving" >}}
-
-- Click **View Logs** in the build progress bar to see the error.
-- Common causes: syntax errors, missing imports, incompatible dependencies.
-- You can fix the code and save again -- each save creates a new version.
-
-{{< /expand >}}
-
-{{< expand "Module not appearing on machine" >}}
-
-- Verify the machine is online and connected to the cloud.
-- Check that you added the module using the **Add to machine** button in the
- code editor, or that the module appears in the machine's configuration.
-- The module version defaults to `latest`. After a successful build, the machine
- picks up the new version automatically within a few minutes.
-
-{{< /expand >}}
-
-{{< expand "DoCommand returns an error" >}}
-
-- Check that all dependencies are configured on the machine and are working.
- Use the test section of each dependency to verify them in isolation.
-- Verify the attribute names in your service configuration match what
- `validate_config` expects.
-- Check the **LOGS** tab for detailed error messages.
-
-{{< /expand >}}
-
-{{< expand "Changes to code not taking effect" >}}
-
-- Make sure the build completed successfully. Check the build progress bar in
- the code editor.
-- The machine must be online to receive the updated module. Check the machine's
- status in the Viam app.
-- By default, modules are configured with version `latest`, which means every
- new build deploys automatically. If the version is pinned, update it manually.
-
-{{< /expand >}}
-
-## What's Next
-
-- [Write a Driver Module](/operate/modules/write-a-driver-module/) -- build a module
- with a typed resource API when you need to manage your own source code and
- build pipeline.
-- [Write a Logic Module](/operate/modules/write-a-logic-module/) -- build control
- logic as an externally managed module with full IDE support.
-- [Deploy a Module](/operate/modules/deploy-a-module/) -- package and upload
- a module to the Viam registry for distribution to other machines or users.
diff --git a/docs/operate/reference/_index.md b/docs/operate/reference/_index.md
deleted file mode 100644
index 07322e7399..0000000000
--- a/docs/operate/reference/_index.md
+++ /dev/null
@@ -1,9 +0,0 @@
----
-linkTitle: "Reference"
-weight: 300
-layout: "empty"
-type: "docs"
-empty_node: true
-header_only: true
-canonical: "/operate/reference/architecture/"
----
diff --git a/docs/operate/reference/advanced-modules/_index.md b/docs/operate/reference/advanced-modules/_index.md
deleted file mode 100644
index 9d35dbcc29..0000000000
--- a/docs/operate/reference/advanced-modules/_index.md
+++ /dev/null
@@ -1,57 +0,0 @@
----
-title: "Advanced modular resources"
-linkTitle: "Advanced modular resources"
-type: docs
-tags:
- [
- "server",
- "rdk",
- "extending viam",
- "modular resources",
- "components",
- "services",
- ]
-description: "Some usage may require you to define new APIs or deploy custom components in non-standard ways."
-aliases:
- - /program/extend/
- - /modular-resources/advanced/
- - /registry/advanced/
-date: "2022-01-01"
-# updated: "" # When the content was last entirely checked
-toc_hide: true
----
-
-Some use cases may require advanced considerations when designing or deploying modular resources.
-Depending on your needs, you may wish to define an entirely new resource API, deploy a custom component using a server on a {{< glossary_tooltip term_id="remote-part" text="remote" >}} {{< glossary_tooltip term_id="part" text="part" >}}, or design a custom ML model.
-
-## New APIs
-
-The [component APIs](/reference/apis/#component-apis) and [service APIs](/reference/apis/#service-apis) provide a standard interface for controlling common hardware components and higher level functionality.
-If your use case aligns closely with an existing API, you should use that API to program your new resource.
-
-If you want to use most of an existing API but need just a few other functions, you can use the `DoCommand` endpoint together with [extra parameters](/reference/sdks/use-extra-params/) to add custom functionality to an existing resource API.
-
-Or, if your resource does not fit into an existing resource API, you can use one of the following:
-
-- If you are working with a component that doesn't fit into any of the existing [component APIs](/reference/apis/#component-apis), you can use the [generic component](/operate/reference/components/generic/) to build your own component API.
-- If you are designing a service that doesn't fit into any of the existing [service APIs](/reference/apis/#service-apis), you can use the [generic service](/reference/apis/services/generic/) to build your own service API.
-
-Both generic resources use the [`DoCommand`](/reference/apis/components/generic/#docommand) endpoint to enable you to make arbitrary calls as needed for your resource.
-
-Alternatively, you can also [define a new resource API](/operate/reference/advanced-modules/create-subtype/) if none of the above options are a good fit for your use case.
-
-## Custom components as remotes
-
-Running {{< glossary_tooltip term_id="modular-resource" text="modular resources" >}} on the computer directly connected to your components is the preferred way of managing and controlling custom components.
-
-However, if you are unable to use modular resources because you need to host `viam-server` on a non-Linux system or have an issue with compilation, you may need to [implement a custom component and register it on a server configured as a remote](/operate/reference/advanced-modules/custom-components-remotes/) on your machine.
-
-## Deploy a module using Docker
-
-If you need to package and deploy a module using Docker, for example if your module relies on complex system dependencies, see [Deploy a module using Docker](/operate/reference/advanced-modules/docker-modules/) for suggestions.
-
-## Design a custom ML model
-
-When working with the [ML model service](/reference/apis/services/ml/), you can deploy an [existing model](/vision/configure/) or [train your own model](/train/custom-training-scripts/).
-
-However, if you are writing your own {{< glossary_tooltip term_id="module" text="module" >}} that uses the ML model service together with the [vision service](/reference/apis/services/vision/), you can also [design your own ML model](/data-ai/reference/mlmodel-design/) to better match your specific use case.
diff --git a/docs/operate/reference/advanced-modules/create-subtype.md b/docs/operate/reference/advanced-modules/create-subtype.md
deleted file mode 100644
index f88c17fb94..0000000000
--- a/docs/operate/reference/advanced-modules/create-subtype.md
+++ /dev/null
@@ -1,122 +0,0 @@
----
-title: "Define a new resource API"
-linkTitle: "Custom API"
-weight: 30
-type: "docs"
-tags: ["rdk", "extending viam", "modular resources", "API"]
-description: "Define a custom API for a resource that does not fit into existing component or service APIs."
-no_list: true
-aliases:
- - /extend/modular-resources/create/create-subtype/
- - /modular-resources/advanced/create-subtype/
- - /registry/advanced/create-subtype/
-date: "2022-01-01"
-# updated: "" # When the content was last entirely checked
----
-
-You can define a new, custom {{< glossary_tooltip term_id="resource" text="resource" >}} API if:
-
-- You have a {{% glossary_tooltip term_id="resource" text="resource" %}} that does not fit into any of the existing {{< glossary_tooltip term_id="component" text="component" >}} or {{< glossary_tooltip term_id="service" text="service" >}} APIs.
-- You have a resource that could fit into an existing API, but you want to define an API with different methods and messages than the one in the existing [APIs](/reference/apis/).
-
-{{% alert title="Tip" color="tip" %}}
-
-Defining a new resource API is significantly more complex than using an existing API.
-In most cases, you should try to use an existing API rather than define a new one.
-
-If you want to use most of an existing API but need just a few other functions, try using the `DoCommand` endpoint and [extra parameters](/reference/sdks/use-extra-params/) to add custom functionality to an existing API.
-For example, if you have a [sensor](/operate/reference/components/sensor/) and you want to define a `Calibrate` method, you can use `DoCommand`.
-
-If your use case uses only `DoCommand` and no other API methods, you can define a new model of [generic component](/operate/reference/components/generic/) or [generic service](/operate/reference/services/generic/).
-
-{{% /alert %}}
-
-## Define your new resource API
-
-Viam uses [protocol buffers](https://protobuf.dev/) for API definition.
-
-To define a new API, you need to define the methods and messages of the API in [protobuf](https://github.com/protocolbuffers/protobuf), write code in Python or Go to implement the higher level server and client functions required, and generate all necessary [protobuf module files](https://buf.build/docs/generate/usage/).
-The following steps guide you through this process in more detail:
-
-1. Decide whether your custom API is a {{< glossary_tooltip term_id="component" text="component" >}} or a {{< glossary_tooltip term_id="service" text="service" >}}.
- If it provides an interface to control hardware, it is a component.
- If it provides higher-level functionality, it is a service.
-1. Choose a name for your API.
- This is called the `{{< glossary_tooltip term_id="subtype" text="subtype" >}}` of your API.
- For example, `gizmo`.
-
- Determine a valid {{< glossary_tooltip term_id="api-namespace-triplet" text="API namespace triplet" >}} based on your API name.
- You can figure out the {{< glossary_tooltip term_id="model-namespace-triplet" text="model namespace triplet" >}} later when you [create a model that implements your custom API](/operate/modules/write-a-driver-module/).
-
- {{< expand "API namespace triplet and model namespace triplet example" >}}
-
- The `viam-labs:audioout:pygame` model uses the module name [audioout](https://github.com/viam-labs/audioout).
- It implements the custom API `viam-labs:service:audioout`:
-
- ```json
- {
- "api": "viam-labs:service:audioout",
- "model": "viam-labs:audioout:pygame"
- }
- ```
-
- For your custom API, your API namespace triplet might be `your-org-namespace:component:gizmo` where `your-org-namespace` is your organization namespace, found in your org settings page.
-
- {{< /expand >}}
-
-1. Create a directory for your module.
- Within that, create a directory called src.
-
- {{% alert title="Tip" color="tip" %}}
-
- If you are writing your module using Python, you can use this [module generator tool](https://github.com/viam-labs/generator-viam-module) to generate stub files for the new API as well as a new {{< glossary_tooltip term_id="module" text="module" >}} that implements the new API.
-
- {{% /alert %}}
-
-1. Define your new API:
-
- - [Write the proto](https://protobuf.dev/programming-guides/proto3/) methods in a `.proto` file inside your src/proto directory.
- For reference:
- - [Example modular component proto file](https://github.com/viamrobotics/viam-python-sdk/blob/main/examples/complex_module/src/proto/gizmo.proto)
- - [Example modular service proto file](https://github.com/viam-labs/speech-service-api/blob/main/src/proto/speech.proto)
- - [Built-in Viam resource proto files](https://github.com/viamrobotics/api/tree/main/proto/viam)
- - And define the proto methods in a protobuf-supported language such as Python or Go in a file called `api.py` or `api.go`, respectively.
- - [Example component in Python](https://github.com/viamrobotics/viam-python-sdk/blob/main/examples/complex_module/src/gizmo/api.py)
- - [Example service in Python](https://github.com/viam-labs/speech-service-api/blob/main/src/speech_service_api/api.py)
-
-1. In the root directory of your module, you need to generate some configuration files.
- You will typically need the following three files for most modules, though different files are required for some advanced use cases.
- See the [Buf documentation](https://buf.build/docs/generate/usage/) for instructions.
-
- - [buf.yaml](https://buf.build/docs/configuration/v1/buf-gen-yaml/)
- - [buf.gen.yaml](https://buf.build/docs/configuration/v1/buf-gen-yaml/)
- - [buf.lock](https://buf.build/docs/configuration/v1/buf-lock/)
-
-1. In the /src/ directory of your module, use the protobuf compiler to [generate](https://buf.build/docs/tutorials/getting-started-with-buf-cli/#generate-code) all other necessary protocol buffer code, based on the `.proto` file you wrote.
- - [Example generated files for a Python-based service](https://github.com/viam-labs/speech-service-api/tree/main/src/proto).
- The `buf.` files were generated.
- The speech.proto was manually written.
-
-## Next steps
-
-{{< cards >}}
-{{% manualcard link="/operate/modules/write-a-driver-module/" %}}
-
-
Implement your API
-
-Now that your resource API is defined, create a new model that implements your new API.
-
-{{% /manualcard %}}
-{{< /cards >}}
-
-{{% alert title="Note" color="note" %}}
-
-After you define a new API and create a model that implements it, keep the following in mind when writing code against your new API:
-
-- You can't use [SDKs](/reference/sdks/) to call your new API unless you build out the client to support it.
- It is easiest to write code against your new API in the language you used to define it.
-- Since your API doesn't have built-in SDK support, you'll need a local copy of the module code on whatever machine is running client code against it.
-- Be sure to import the API definition from the module directory, for example in Python it would be of the form `from path.to.module.src.gizmo import Gizmo`.
- This example module written in Python uses a [Python script](https://github.com/viamrobotics/viam-python-sdk/blob/main/examples/complex_module/client.py) to call the custom API from within the module directory.
-
-{{% /alert %}}
diff --git a/docs/operate/reference/advanced-modules/custom-components-remotes.md b/docs/operate/reference/advanced-modules/custom-components-remotes.md
deleted file mode 100644
index 8c10f98573..0000000000
--- a/docs/operate/reference/advanced-modules/custom-components-remotes.md
+++ /dev/null
@@ -1,72 +0,0 @@
----
-title: "Add custom components as remote parts"
-linkTitle: "Custom components as remote parts"
-weight: 99
-type: "docs"
-tags: ["server", "sdk"]
-aliases:
- - /program/extend/sdk-as-server/
- - /program/extend/custom-components-remotes/
- - /extend/custom-components-remotes/
- - /modular-resources/advanced/custom-components-remotes/
- - /registry/advanced/custom-components-remotes/
-description: "If you are unable to use modular resources, you can implement custom components and register them on a server configured as a remote of your machine."
-date: "2022-01-01"
-# updated: "" # When the content was last entirely checked
----
-
-Running {{< glossary_tooltip term_id="modular-resource" text="modular resources" >}} on the computer directly connected to your components is the preferred way of managing and controlling custom components.
-
-However, if you are unable to use [modular resources](/operate/modules/write-a-driver-module/) because you need to host `viam-server` on a non-Linux system or have an issue with compilation, you can use a [Viam SDK](/reference/sdks/) to code a custom resource implementation, host it on a server, and add it as a [remote part](/operate/reference/architecture/parts/) of your machine.
-
-Once you have coded your custom component and configured the remote servers, you can control and monitor your component with the Viam SDKs, like any other component.
-
-To show how to create a custom resource, the following example creates an arm as a custom component and registers the new arm model with a Viam SDK.
-Then you can control it as part of your machine with the same [API methods](/reference/apis/components/arm/#api) available for [arm models built into the RDK](/operate/reference/components/arm/#configuration).
-
-## Instructions
-
-To add a custom resource as a [remote part](/operate/reference/architecture/parts/):
-
-{{< tabs >}}
-{{% tab name="Go" %}}
-
-1. Code a new model of a built-in resource type.
- You can do this by creating a new interface that implements required methods.
- The new model must implement any functions of the built-in resource type marked as required in its [RDK API definition](/reference/apis/).
-2. Register the custom component on a new gRPC server instance and start the server.
-3. Add the server as a [remote part](/operate/reference/architecture/parts/) of your machine.
-4. (Optional) Ensure the remote server automatically starts when the machine boots.
-
-Each remote server can host one or many custom components.
-
-{{% /tab %}}
-{{% tab name="Python" %}}
-
-{{< alert title="Tip" color="tip" >}}
-For more detailed instructions, see the full example in the [Python SDK documentation](https://python.viam.dev/examples/example.html#subclass-a-component).
-{{< /alert >}}
-
-1. Code a new model of a built-in resource type.
- You can do this by subclassing a built in resource type like `sensor` or `arm`.
- The new model must implement any methods of the built-in resource type marked as required in its [RDK API definition](/reference/apis/).
-1. Register the custom component on a new gRPC server instance and start the server.
- You can do this with the [`viam.rpc` library](https://python.viam.dev/autoapi/viam/rpc/index.html) by creating a new `rpc.server.Server` instance.
-1. Add the server as a [remote part](/operate/reference/architecture/parts/) of your machine.
-1. (Optional) Ensure the remote server automatically starts when the machine boots.
-
-Each remote server can host one or many custom components.
-
-{{% /tab %}}
-{{% /tabs %}}
-
-{{% alert title="Important" color="note" %}}
-
-You must define all methods belonging to a built-in resource type when defining a new model.
-Otherwise, the class will not instantiate.
-
-- If you are using the Python SDK, raise an `NotImplementedError()` in the body of functions you do not want to implement or put `pass`.
-- If you are using the Go SDK, return `errUnimplemented`.
-- Additionally, return any values designated in the function's return signature, typed correctly.
-
-{{% /alert %}}
diff --git a/docs/operate/reference/advanced-modules/docker-modules.md b/docs/operate/reference/advanced-modules/docker-modules.md
deleted file mode 100644
index 0ab8094aca..0000000000
--- a/docs/operate/reference/advanced-modules/docker-modules.md
+++ /dev/null
@@ -1,74 +0,0 @@
----
-title: "Deploy a module using Docker"
-linkTitle: "Docker Modules"
-weight: 300
-type: "docs"
-tags: ["extending viam", "modular resources"]
-description: "Deploy a module using Docker."
-no_list: true
-date: "2025-04-10"
-# updated: "" # When the content was last entirely checked
----
-
-In rare cases, you may need to package and deploy a module using Docker.
-Use cases for this include:
-
-- Your module has complex system dependencies that cannot be easily installed on a machine.
-- You use a large container image and some layers are already used by your machine which means layer caching can reduce the size of the download.
-- You have specific security requirements that are difficult to meet with the default module deployment.
-
-If you choose to deploy your module using Docker, we recommend creating a "first run" script or binary to run any necessary setup steps.
-Note this is _not_ recommended for modules that do not use Docker, as it adds unnecessary complexity.
-
-## Use a `first_run` script or binary
-
-1. Create a tarball that contains:
-
- - The module's entrypoint script or binary, for example:
-
- ```sh {id="terminal-prompt" class="command-line" data-prompt="$"}
- #!/bin/bash
- exec docker run
- ```
-
- - A [meta.json](/operate/modules/advanced/metajson/)
- - A first run script or binary that will be executed during the setup phase, for example:
-
- ```sh {id="terminal-prompt" class="command-line" data-prompt="$"}
- #!/usr/bin/env bash
-
- docker pull mongo:6
-
- cat << EOF
- -------------------------------------
- The setup script ran successfully!
- -------------------------------------
- EOF
-
- exit 0
- ```
-
- [This example Makefile on GitHub](https://github.com/viam-labs/wifi-sensor/blob/7823b6ad3edcbbbf20b06c34b3181453f5f3f078/Makefile) builds a module binary and bundles it along with a meta.json and first run script.
-
-1. Edit the meta.json to include a `first_run` field that points to the first run script or binary.
-
- ```json
- {
- ...
- "first_run": "first_run.sh"
- }
- ```
-
-1. Configure your module on your machine in the same way you would configure a regular module.
- The first run script will execute once when `viam-server` receives a new configuration.
- It will only execute once per module or per version of the module.
-
-1. (Optional) After a first run script runs successfully, Viam adds a marker file in the module's data directory.
- The marker file path format is of the form `unpackedModDir + FirstRunSuccessSuffix`.
- For example, `.viam/packages/data/module/abcd1234-abcd-abcd-abcd-abcd12345678-viam-rtsp-0_1_0-linux-amd64/bin.first_run_succeeded`.
-
- If you want to force a first run script to run again without changing the configured module or module version, you can do so by deleting this file.
-
-1. (Optional) By default, a first run script will timeout after 1 hour.
- This can be adjusted by adding a `first_run_timeout` to the module's configuration.
- For example, `"first_run_timeout": "5m"` will lower the script timeout to 5 minutes.
diff --git a/docs/operate/reference/architecture/_index.md b/docs/operate/reference/architecture/_index.md
deleted file mode 100644
index c3914a6976..0000000000
--- a/docs/operate/reference/architecture/_index.md
+++ /dev/null
@@ -1,214 +0,0 @@
----
-title: "Viam architecture"
-linkTitle: "Viam architecture"
-weight: 10
-type: "docs"
-description: "How a machine running on Viam is structured, from on-device to cloud communications."
-imageAlt: "Viam architecture"
-images: ["/viam/machine-components.png"]
-tags: ["components", "services", "communication"]
-date: "2024-08-13"
-aliases:
- - /architecture/
-# updated: "" # When the content was last entirely checked
----
-
-This page provides an overview of how a machine is structured, including on-device and cloud communications:
-
-- [`viam-server` and `viam-micro-server`](#viam-server-and-viam-micro-server)
-- [Components, services and modules](#components-services-modules)
-- [Communication flow and security](#communication)
-- [How data flows in Viam](#data-management-flow)
-- [Basic machine example](#basic-machine-example)
-- [Structure of more complex machines](#complex-machines-with-multiple-parts)
-
-{{}}
-
-## `viam-server` and `viam-micro-server`
-
-`viam-server` is the open-source executable binary that runs on your machine's SBC or other computer.
-
-`viam-server` does the following locally:
-
-- Runs drivers for your hardware
-- Runs motion planning and vision services
-- Runs {{< glossary_tooltip term_id="module" text="modules" >}}
-- Manages local connections between all these resources
-- Captures and stores data
-
-When `viam-server` can connect to the cloud, it also:
-
-- Automatically pulls configuration updates you make
-- Gets new versions of software packages
-- Uploads and syncs image and sensor data
-- Handles requests from client code you write with [SDKs](/reference/sdks/)
-- Allows you to remotely monitor and control your machine
-
-`viam-server` can use the internet, wide area networks (WAN) or local networks (LAN) to establish peer-to-peer connections between two {{< glossary_tooltip term_id="machine" text="machines" >}}, or to a client application.
-
-[`viam-micro-server`](/operate/reference/viam-micro-server/) is the lightweight version of `viam-server` that you can run on ESP32 microcontrollers.
-It supports a limited set of {{< glossary_tooltip term_id="resource" text="resources" >}} and can connect with the cloud as well as with devices running `viam-server`.
-
-## Components, services, modules
-
-A {{< glossary_tooltip term_id="component" text="component" >}} represents a physical piece of hardware in your {{< glossary_tooltip term_id="machine" text="machine" >}}, and the software that directly supports that hardware.
-
-A {{< glossary_tooltip term_id="service" text="service" >}} is a software package that adds complex capabilities such as motion planning or object detection to your machine.
-
-Viam has many built-in components and services that run within `viam-server`.
-
-A _modular resource_ is a custom component or service, not built into `viam-server` but rather provided by a _module_ that you or another user have created.
-A module runs as a process managed by `viam-server` on your machine, communicating over UNIX sockets, and `viam-server` manages its lifecycle.
-
-{{}}
-
-{{< expand "Click for an example" >}}
-
-Imagine you have a wheeled rover with two motors, a GPS unit, and a camera, controlled by a single-board computer (SBC) such as a Raspberry Pi.
-
-The motors, GPS, and camera each require software drivers so that the board can send signals to them and receive signals from them.
-These drivers are called _{{< glossary_tooltip term_id="component" text="components" >}}_.
-There is also a _component_ for the board itself that allows Viam software to communicate with the pins on the board.
-
-If you configure a [base component](/operate/reference/components/base/), you can specify the size of the wheels attached to the motors, and how wide the rover base is, so `viam-server` can calculate how to coordinate the motion of the rover base.
-
-If your rover includes a piece of hardware (such as a particular sensor) that is not yet supported as a built-in in by Viam, check the registry for a contributed {{< glossary_tooltip term_id="module" text="module" >}} or write your own module to integrate it into your machine.
-
-Say you want your machine to navigate intelligently between GPS coordinates.
-Instead of writing code from scratch, you can use Viam's built-in navigation service by adding it to your configuration, specifying how the GPS and any additional movement sensors are oriented with respect to your hardware.
-Since your configuration includes information about the wheeled base of your rover as well as how that base is oriented in relation to your GPS and other sensors, `viam-server` can use the motion and navigation services to calculate how much power to send to each motor to get it to a point on the map.
-
-If you want to add some other high-level software functionality beyond the built-in services (for example, your own flavor of navigation service), you can add your own service with a module.
-
-{{< /expand >}}
-
-## Communication
-
-
-
-Viam uses peer-to-peer communication, where all machines running `viam-server` or [`viam-micro-server`](/operate/reference/viam-micro-server/) (the version of `viam-server` for microcontrollers) communicate directly with each other as well as with the cloud.
-This peer-to-peer connectivity is enabled by sending [gRPC commands over WebRTC connections](/operate/reference/architecture/machine-to-machine-comms/#low-level-inter-robotsdk-communication).
-
-On startup, `viam-server` establishes a {{< glossary_tooltip term_id="webrtc" text="WebRTC" >}} connection with Viam.
-`viam-server` pulls its configuration from the app, caches it locally, and initializes all components and services based on that configuration.
-
-If [sub-parts or remote parts](#complex-machines-with-multiple-parts) are configured, communications are established between the `viam-server` instances on each of them.
-
-If you have client code running on a separate computer, that code sends API requests to `viam-server` using gRPC over WebRTC.
-If a WebRTC connection cannot be established, the request is sent directly over gRPC.
-The Viam cloud works as the signaling server for connections where possible.
-If you are connecting to a machine over local network or in offline mode, `viam-server` functions as the signaling server instead.
-
-When a built-in service communicates with a component, for example when the vision service requests an image from a camera, `viam-server` handles that request as well.
-
-When you control your machine or view its camera streams or sensor outputs from the **CONTROL** tab, those connections happen over WebRTC.
-The web UI uses the same API endpoints as your SDK client code (in fact, it uses the Viam TypeScript SDK), with `viam-server` handling requests.
-
-{{% alert title="Protobuf APIs" color="info" %}}
-All Viam APIs are defined with the [Protocol Buffers (protobuf)](https://protobuf.dev/) framework.
-{{% /alert %}}
-
-For more details, see [Machine-to-Machine Communication](/operate/reference/architecture/machine-to-machine-comms/).
-
-### Security
-
-TLS certificates automatically provided by Viam ensure that all communication is authenticated and encrypted.
-
-Viam uses API keys with [role-based access control (RBAC)](/manage/manage/rbac/) to control access to machines from client code.
-
-## Data management flow
-
-{{}}
-
-
-Data is captured and synced to the Viam Cloud as follows:
-
-1. Data collected by your resources, such as sensors and cameras, is first stored locally in a specified directory (defaults to ~/.viam/capture).
- You control what data to capture, how often to capture it, and where to store it using the configuration.
-
- - You can also sync data from other sources by putting it into folders you specify.
-
-
-1. `viam-server` syncs data to the cloud at your specified interval, and deletes the data from the local directory.
-
-1. You can view your data in the web UI or query it using Viam SDKs, MQL, or SQL.
-
-If a device has intermittent internet connectivity, data is stored locally until the machine can reconnect to the cloud.
-
-For more information, see [Data management service](/data/capture-sync/capture-and-sync-data/).
-
-## Basic machine example
-
-
-{{}}
-
-
-Imagine you have a simple device consisting of a temperature sensor connected to the GPIO pins of a single-board computer (SBC).
-You want to capture sensor data at regular intervals, and sync it to the cloud.
-Here is how this works in Viam:
-
-- You configure your machine in the web UI with a sensor {{< glossary_tooltip term_id="component" text="component" >}} and the data management {{< glossary_tooltip term_id="service" text="service" >}}.
-- `viam-server` runs on the SBC, managing all communications between hardware and the cloud using gRPC over {{< glossary_tooltip term_id="webrtc" text="WebRTC" >}}.
- On startup, `viam-server` uses credentials stored locally to establish a connection with Viam and fetches its configuration.
-- Sensor data is cached in a local folder, then synced to the cloud at a configurable interval.
-- You can use the tools on Viam to remotely view sensor data as well as to change your machine's configuration, to view logs, and more.
-
-Now imagine you want to run code to turn on a fan when the temperature sensor reads over 100 degrees Fahrenheit:
-
-- Configure the fan motor as a motor component and wire the fan motor relay to the same board as the sensor.
-- Write your script using one of the Viam [SDKs](/reference/sdks/), for example the Viam Python SDK, using the sensor API and motor API.
-- You then run this code either locally on the SBC, or on a separate server.
- See [Create a headless app](/operate/control/headless-app/) for more information.
- Your code connects to the machine, authenticating with API keys, and uses the [sensor API](/operate/reference/components/sensor/#api) to get readings and the [motor API](/operate/reference/components/motor/#api) to turn the motor on and off.
-
- {{< imgproc src="/build/program/sdks/robot-client.png" resize="x400" declaredimensions=true alt="A desktop computer (client in this case) sends commands to robot 1 (server) with gRPC over wifi." >}}
-
-Now, imagine you want to change to a different model of temperature sensor from a different brand:
-
-- You power down your device, disconnect the old sensor from your SBC and connect the new one.
-- You update your configuration in the web UI to indicate what model you are using, and how it's connected (imagine this one uses USB instead of GPIO pins).
-- You turn your device back on, and `viam-server` automatically fetches the config updates.
-- You do not need to change your control code, because the API is the same for all models of sensor.
-
-## Complex machines with multiple parts
-
-In Viam, a _{{< glossary_tooltip term_id="part" text="part" >}}_ is an organizational concept consisting of one instance of `viam-server` (or `viam-micro-server`) running on a SBC or other computer, and all the hardware and software that the `viam-server` instance controls.
-
-Many simple {{< glossary_tooltip term_id="machine" text="machines" >}} consist of only one part: just one computer running `viam-server` with configured components and services.
-If you have a more complex situation with multiple computers and associated hardware working together, you have two options for organization:
-
-- One complex {{< glossary_tooltip term_id="machine" text="machine" >}} consisting of multiple parts, working together.
-- Multiple individual machines (each made up of one or more parts), linked by a {{< glossary_tooltip term_id="remote-part" text="remote" >}} connection.
-
-These two options are very similar: in both cases, the parts communicate with each other securely and directly using gRPC/{{< glossary_tooltip term_id="webrtc" text="WebRTC" >}}.
-Any given part can be a remote part of multiple machines, whereas a part can only be a sub-part of one machine.
-In other words, remote connections allow sharing of resources across multiple machines, whereas main parts and sub-parts are a way to hierarchically organize one machine.
-
-Connecting parts (either as main part and sub-part, or as part and remote part) means that you can write control code that establishes a connection with the main part and controls all parts in a coordinated way.
-This streamlines authentication because you do not need to provide multiple sets of API keys as you would if you were using separate API clients.
-However, in some high-bandwidth cases it is better to establish a direct connection from an API client to a part, because connections to remotes and to sub-parts use the main part's bandwidth.
-
-{{< expand "Multi-part and remote examples" >}}
-
-- **Compute power example:** Imagine you have a robotic arm with a camera on it, connected to a SBC such as a Raspberry Pi.
- You want to use the Viam motion service to control the arm, and you want to use the vision service on the output from the camera, but the SBC does not have the compute power to plan complex motion and also interpret camera output quickly.
- You can set up a second {{< glossary_tooltip term_id="part" text="part" >}} on a server, as a sub-part of the same machine, and offload the heavy compute to that part.
-
-- **One weather station to many boats:** Imagine you have one weather station collecting data, and multiple boats (perhaps some in different {{< glossary_tooltip term_id="organization" text="organizations" >}}) whose behavior depends on that weather data.
- You can set up a remote connection from each of the `viam-server` instances running on the boats to the `viam-server` instance on the weather station and get that data more directly, without having to wait for the data to sync to the cloud.
-
-- **One camera to many rovers:** Imagine you have a fleet of rovers in a factory.
- One camera has an overhead view of the entire factory floor.
- The camera is set up as one machine.
- Each of the rovers is a separate machine, and they can each have the camera configured as a remote so that they can access the overhead camera stream.
-
-{{< /expand >}}
-
-See [Parts, Sub-parts and Remotes](/operate/reference/architecture/parts/) for more details.
-
-## Next steps
-
-This page has provided an overview of the architecture of just one machine.
-For information on organizing a fleet of many machines, see [Organize your machines](/manage/reference/organize/).
-
-For more details on the architecture of a single machine, see the following:
diff --git a/docs/operate/reference/architecture/machine-to-machine-comms.md b/docs/operate/reference/architecture/machine-to-machine-comms.md
deleted file mode 100644
index de33640719..0000000000
--- a/docs/operate/reference/architecture/machine-to-machine-comms.md
+++ /dev/null
@@ -1,129 +0,0 @@
----
-title: "Machine-to-Machine Communication: End-to-End Flow"
-linkTitle: "Machine-to-Machine Communication"
-weight: 60
-type: "docs"
-description: "Explanation of how a machine and its parts interact at the communication layer."
-aliases:
- - /internals/robot-to-robot-comms/
- - /internals/machine-to-machine-comms/
- - /architecture/machine-to-machine-comms/
-toc_hide: true
----
-
-When building a smart machine application in, a user typically begins by configuring their machine which can consist of one or more {{< glossary_tooltip term_id="part" text="parts" >}}.
-Next they will test that it is wired up properly using the Control page.
-Once they've ensured everything is wired up properly, they will build their main application and the business logic for their machine using one of Viam's language SDKs.
-This SDK-based application is typically run on either the main part of the machine or a separate computer dedicated to running the business logic for the machine.
-
-Below, we describe the flow of information through a Viam-based multipart machine and then get into the specifics of what backs these connections and communications APIs.
-
-## High-level inter-robot/SDK communication
-
-To begin, let's define our machine's topology:
-
-
-
-This machine is made of two parts and a separate SDK-based application, which we'll assume is on a third machine, though it could just as easily run on the main part without any changes.
-
-- The first and main part, RDK Part 1, consists of a Raspberry Pi and a single USB connected camera called Camera.
-
-- The second and final part, RDK Part 2, consists of a Raspberry Pi connected to a robotic arm over ethernet and a gantry over GPIO.
-
-RDK Part 1 will establish a bidirectional gRPC/{{< glossary_tooltip term_id="webrtc" >}} connection to RDK Part 2.
-RDK Part 1 is considered the controlling peer (client).
-RDK Part 2 is consider the controlled peer (server).
-
-Let's suppose our SDK application uses the camera to track the largest object in the scene and instructs the arm to move to that same object.
-
-Since RDK Part 1 is the main part and has access to all other parts, the application will connect to it using the SDK.
-Once connected, it will take the following series of actions:
-
-
-
Get segmented point clouds from the camera and the object segmentation service.
-
-
Find the largest object by volume.
-
-
Take the object's center pose and tell the motion service to move the arm to that point.
-
-
Go back to 1.
-
-Let's breakdown how these steps are executed.
-
-
-
Get segmented point clouds from the camera and the object segmentation service:
-
-
-
-
-
The SDK will send a GetObjectPointClouds request with Camera being referenced in the message to RDK Part 1's Object Segmentation Service.
-
-
RDK Part 1 will look up the camera referenced, call the GetPointCloud method on it.
-
-
The Camera will return the PointCloud data to RDK Part
-
-
RDK Part 1 will use a point cloud segmentation algorithm to segment geometries in the PointCloud.
-{{% alert title="Important" color="note" %}}
-The points returned are respective to the reference frame of the camera.
-This will become important in a moment.
-{{% /alert %}}
-
The set of segmented point clouds and their bounding geometries are sent back to the SDK-based application.
-
-
-
Find the largest object by volume:
-
-
The application will iterate over the geometries of the segmented point clouds returned to it and find the object with the greatest volume and record its center pose.
-
-
-
Take the object's center pose and tell the motion service to move the arm to that point:
-
-
-
-
-
The SDK application will send a Move request for the arm to the motion service on RDK Part 1 with the destination set to the center point determined by the application.
-
-
RDK Part 1's motion service will break down the Move request and perform the necessary frame transforms before sending the requests along to the relevant components.
-This is where the frame system comes into play.
-Our center pose came from the camera but we want to move the arm to that position even though the arm lives in its own frame.
-The frame system logic in the RDK automatically handles the transformation logic between these two reference frames while also handling understanding how the frame systems are connected across the two parts.
-
-
Having computed the pose in the reference frame of the arm, the motion service takes this pose, and sends a plan on how to move the arm in addition to the gantry to achieve this new goal pose to RDK Part 2.
-The plan consists of a series of movements that combine inverse kinematics, mathematics, and constraints based motion planning to get the arm and gantry to their goal positions.
-
-
In executing the plan, which is being coordinated on RDK Part 1, Part 1 will send messages to the Arm and Gantry on RDK Part 2.
-RDK Part 2 will be unaware of the actual plan and instead will only receive distinct messages to move the components individually.
-
-
The arm and gantry connected to RDK Part 2 return an acknowledgement of the part Move requests to RDK Part 2.
-
-
RDK Part 2 returns an acknowledgement of the Motion Move request to RDK Part 1.
-
-
RDK Part 1 returns an acknowledgement of the Motion Move request to the SDK application.
-
-
-
-## Low-level inter-robot/SDK communication
-
-All component and service types in the RDK, and the Viam API for that matter, are represented as [Protocol Buffers (protobuf)](https://developers.google.com/protocol-buffers) services.
-protobuf is a battle tested Interface Description Language (IDL) that allows for specifying services, their methods, and the messages that comprise those methods.
-Code that uses protobuf is autogenerated and compiles messages into a binary form.
-
-[gRPC](https://grpc.io/) is responsible for the transport and communication of protobuf messages when calling protobuf methods.
-It generally works over a TCP, TLS backed HTTP2 connection operating over framing see [gRPC's HTTP2 documentation](https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md) for more.
-
-The RDK uses protobuf and gRPC to enable access and control to its components and services.
-That means if there are two arms in a machine configuration, there is only one Arm service that handles the Remote Procedure Calls (RPC) for all arms configured.
-
-In addition to gRPC, the RDK uses [WebRTC](https://webrtcforthecurious.com/) video and audio streams and data channels to enable peer to peer (P2P) communication between machine parts as well as SDKs and the Remote Control interface.
-
-An outline of how WebRTC is used lives on [Go.dev](https://pkg.go.dev/go.viam.com/utils@v0.0.3/rpc#hdr-Connection), but in short, an RDK is always waiting on [app.viam.com](https://app.viam.com) to inform it of a connection requesting to be made to it whereby it sends details about itself and how to connect on a per connection basis.
-Once a connection is made, [app.viam.com](https://app.viam.com) is no longer involved in any packet transport and leaves it up to the two peers to communicate with each other.
-
-### Security implications
-
-Generally you do not need to open any ports or change your network configuration, if:
-
-- machines are on the internet and can successfully talk with `app.viam.com` and WebRTC works or
-- machines are on the same local network and mDNS and WebRTC are working
-
-If WebRTC is not available, you can communicate with `viam-server` over a gRPC connection.
-The default port is 8080 but you can configure another port in the [network settings](/manage/fleet/system-settings/#configure-bind-address-and-port).
diff --git a/docs/operate/reference/architecture/parts.md b/docs/operate/reference/architecture/parts.md
deleted file mode 100644
index f165560e29..0000000000
--- a/docs/operate/reference/architecture/parts.md
+++ /dev/null
@@ -1,158 +0,0 @@
----
-title: "Machine architecture: Parts"
-linkTitle: "Parts, sub-parts, remotes"
-weight: 40
-type: "docs"
-description: "Connect multiple machine parts to each other as sub-parts or remotes."
-tags: ["server", "components", "services"]
-aliases:
- - /manage/parts-and-remotes/
- - /build/configure/parts-and-remotes/
- - /configure/parts/
- - /build/configure/parts/
- - /architecture/parts/
-date: "2022-01-01"
-# updated: "" # When the content was last entirely checked
----
-
-When {{< glossary_tooltip term_id="machine" text="smart machines" >}} communicate with each other, they can share resources and operate collaboratively.
-This document explains how to establish secure connections between machines.
-
-## Machine parts
-
-Machines are organized into _parts_, where each part represents a computer (a single-board computer, desktop, laptop, or other computer) running [`viam-server`](/operate/reference/viam-server/), the hardware {{< glossary_tooltip term_id="component" text="components" >}} attached to it, and any {{< glossary_tooltip term_id="service" text="services" >}} or other {{< glossary_tooltip term_id="resource" text="resources" >}} running on it.
-
-Every smart machine has a main part which is automatically created when you create the machine.
-Multi-part machines also have one or more _sub-parts_ representing additional computers running `viam-server`.
-
-There are two ways to link machine parts:
-
-- **Sub-part**: If you have multiple computers within the _same machine_, use one as the main part and [connect each additional part to it as a sub-part](#configure-a-sub-part).
- Any given part can only be a sub-part of one main part.
-
-
- Click for an example.
- Imagine you have a system of five cameras in different places along an assembly line, each attached to its own single-board computer, and you want to run an object detector on the streams from all of them.
- You have one main computer with greater compute power set up as the main part.
- You set up each of the single-board computers as a sub-part.
- This allows the main part to access all the camera streams and run object detection on all of them.
- You could also set this up with each single-board computer being a remote part instead of a sub-part, but it is slightly easier to configure sub-parts because you do not need to add the address of each part to your machine's config.
- Additionally, configuring a discrete system of parts as one multi-part machine helps keep your fleet more clearly organized.
-
-
-- **Remote part**: To connect multiple computers that are parts of _different machines_ in the same or different organizations, [add one machine part as a remote part of the other machine or machines](#configure-a-remote-part).
- A part can be a remote part of any number of other parts.
-
-
- Click for an example.
- If you have one camera connected to a computer in a warehouse that many machines should be able to share, you can configure the camera as a remote part of each machine that needs it.
-
-
-Connections between machines are established using the best network path available.
-
-When you configure a remote part or a sub-part, the main machine part can access all the components and services configured on the remote machine part as though they were entities of the main machine part.
-This is a one-way connection: the main machine part can access the resources of the remote machine part, but the remote machine cannot access the resources of the machine part remoting into it.
-
-When a part starts up, it attempts to connect to any remotes and sub-parts.
-If it cannot connect to them, the part will still successfully start up.
-
-
-
-### Find part ID
-
-To copy the ID of your machine part, select the part status dropdown to the right of your machine's location and name on the top of its page and click the copy icon next to **Part ID**.
-
-For example:
-
-{{}}
-
-## Configuration
-
-### Configure a sub-part
-
-You can make a multi-part machine by first configuring one part which is the "main" part, and then configuring one or more sub-parts.
-The main part will be able to access the resources of its sub-parts.
-Sub-parts will _not_ have access to the resources of the main part.
-
-Viam automatically creates the main part for you when you create a new {{< glossary_tooltip term_id="machine" text="machine" >}}.
-To add a new sub-part:
-
-1. Navigate to the **CONFIGURE** tab of your machine's page.
-2. Click the **+** (Create) icon next to the name of your main part, then click **Sub-part** from the menu:
-
- {{}}
-
-3. Save the config.
-
-{{% hiddencontent %}}
-The sub-part will not be visible as a `remote` in the debug config until after you use the setup instructions to make the sub-part go live.
-{{% /hiddencontent %}}
-
-To rename or delete a sub-part, or to make it the main part, click the **...** menu:
-
-{{}}
-
-By default, all sub-parts appear in the [frame system](/operate/reference/services/frame-system/) at the world origin.
-You can specify translations for sub-parts by configuring their frames with appropriate translation values relative to their parent frame.
-You can view the `frame` of each sub-part in the `remotes` section of the debug config.
-
-### Configure a remote part
-
-To establish a connection between a part of one machine and a part of a second machine, add one as a remote part in the other machine part's config:
-
-1. Go to the machine page of the machine part from which you want to establish a remote connection.
- This is the machine part that will be able to access the resources of the other machine part.
-1. Navigate to the **CONFIGURE** tab, click the **+** (Create) icon next to the machine part's name in the left side menu.
-
- {{}}
-
-1. Click **Remote part**.
-1. Select the remote part from the list of parts.
-
- Alternatively, click **Add empty remote** and then scroll to the newly-created remote part configuration card.
- Click on **JSON** and replace the JSON object with a remote config.
-
- {{}}
-
- {{< expand "Click to see how to get a remote config object" >}}
-
-1. Go to the machine page of the smart machine part to which you wish to establish the remote connection.
- This is the machine part whose resources will be accessible to the other machine part.
-1. Navigate to the **CONNECT** tab.
-1. Click **Configure as a remote part** in the left-hand menu.
-1. Copy the entire JSON snippet including the name, address, and authentication credentials of the remote part.
-
- {{% snippet "show-secret.md" %}}
-
- {{< /expand >}}
-
- Remotes have the following parameters:
-
-
- | Name | Type | Required? | Description |
- | ------------------ | ------ | --------- | ----------- |
- | `name` | string | Optional | The name of the remote part. |
- | `address` | string | Optional | The address of the remote part. |
- | `auth` | string | Object | The authentication credentials of the remote part. For example: `{ "credential": { "type": "api-key", "payload": "abcdefghijklmnop123456789abcdefg" } }`. |
- | `prefix` | string | Optional | If set, all resource names fetched from the remote part have the prefix. For example, a component with the name `arm-1` configured on a remote part with the configuration `"prefix": "test123"` returns the name `test123arm-1`. |
- | `notes` | string | Optional | Descriptive text to document the purpose, configuration details, or other important information about this remote part. |
-
-1. Click **Save** in the upper right corner of the page to save your config.
-
-## Using remote parts and sub-parts with the Viam SDKs
-
-Once your sub-part or remote part is configured, you can access all the components and services configured on the sub-part or remote machine part as though they were resources of your main machine part.
-The only difference is if the remote machine part has a configured prefix.
-If there is a prefix, you must prepend it to the resource name:
-
-For example, to access a servo called `my_servo` on a sub-part or a remote without configured `prefix`, you need to call:
-
-```python
-servo = Servo.from_robot(robot=robot, name='my_servo')
-```
-
-For a remote part with a `prefix` `my_remote`, instead use:
-
-```python
-servo = Servo.from_robot(robot=robot, name='my_remotemy_servo')
-```
diff --git a/docs/operate/reference/components/_index.md b/docs/operate/reference/components/_index.md
deleted file mode 100644
index 80fb4e2b50..0000000000
--- a/docs/operate/reference/components/_index.md
+++ /dev/null
@@ -1,11 +0,0 @@
----
-title: "Components"
-linkTitle: "Components"
-weight: 20
-type: docs
-layout: "empty"
-canonical: "/operate/reference/components/arm/"
-no_component: true
-empty_node: true
-toc_hide: false
----
diff --git a/docs/operate/reference/components/arm/_index.md b/docs/operate/reference/components/arm/_index.md
deleted file mode 100644
index 998ead05be..0000000000
--- a/docs/operate/reference/components/arm/_index.md
+++ /dev/null
@@ -1,102 +0,0 @@
----
-title: "Arm Component"
-linkTitle: "Arm"
-childTitleEndOverwrite: "Arm Component"
-weight: 10
-type: "docs"
-description: "A robotic arm is made up of a series of links and joints, ending with a device you can position."
-no_list: true
-tags: ["arm", "components"]
-icon: true
-images: ["/icons/components/arm.svg"]
-modulescript: true
-aliases:
- - /components/arm/
-hide_children: true
-outputs:
- - html
- - typesense
-date: "2024-10-21"
-# SME: Peter L
----
-
-The arm component provides an API for linear motion, including self-collision prevention.
-
-If you have a physical robotic arm, consisting of a serial chain of joints and links, with a fixed end and an end effector end, use an arm component.
-
-Arms have two ends: one fixed in place, and one with a device you can position.
-When controlling an arm, you can place its end effector at arbitrary cartesian positions relative to the base of the arm.
-
-## Configuration
-
-For an end-to-end guide that configures an arm and drives it through the motion service, see [Move your first arm](/motion-planning/quickstarts/first-arm/).
-
-To use a robotic arm, you need to add it to your machine's configuration.
-Go to your machine's **CONFIGURE** page, and add a model that supports your arm.
-
-The following list shows the available arm models.
-For additional configuration information, click on the model name:
-
-{{< tabs >}}
-{{% tab name="viam-server" %}}
-
-{{}}
-
-{{< alert title="Add support for other models" color="tip" >}}
-
-If none of the existing models fit your use case, you can create a {{< glossary_tooltip term_id="modular-resource" text="modular resource" >}} to add support for it.
-
-{{< /alert >}}
-
-{{% /tab %}}
-{{% tab name="Micro-RDK" %}}
-
-{{< alert title="Support Notice" color="note" >}}
-
-There is currently no support for this component compatible with the Micro-RDK.
-
-{{< /alert >}}
-
-{{% /tab %}}
-{{< /tabs >}}
-
-## API
-
-The [arm API](/reference/apis/components/arm/) supports the following methods:
-
-{{< readfile "/static/include/components/apis/generated/arm-table.md" >}}
-
-## Motion planning with your arm
-
-The arm API sends requests for the arm to move to a set of joint positions, and reports the arm's current joint positions.
-Viam's motion service provides an [API for moving the end of the arm to a given position, around any obstacles](/operate/reference/services/motion/#api).
-
-For each arm model, there is a JSON file that describes the [kinematics parameters of the arm](/operate/reference/kinematic-chain-config/).
-When you configure an arm model, the arm driver parses the kinematics file for the [frame system](/operate/reference/services/frame-system/) service to use.
-The frame system allows the motion service to calculate where any component of your machine is relative to any other component, other machine, or object in the environment.
-
-## Troubleshooting
-
-If your arm is not working as expected, follow these steps:
-
-1. Check your machine logs on the **LOGS** tab to check for errors.
-1. Review your arm model's documentation to ensure you have configured all required attributes.
-1. Click on the **TEST** panel on the **CONFIGURE** or **CONTROL** tab and test if you can use the arm there.
-1. Disconnect and reconnect your arm.
-
-If none of these steps work, reach out to us on the [Community Discord](https://discord.gg/viam) and we will be happy to help.
-
-## Next steps
-
-For general configuration, development, and usage info, see:
-
-{{< cards >}}
-{{% card link="/operate/modules/configure-modules/" noimage="true" %}}
-{{% card link="/operate/control/headless-app/" noimage="true" %}}
-{{% card link="/motion-planning/move-an-arm/move-to-pose/" noimage="true" %}}
-{{< /cards >}}
-
-You can also use the arm component with the following services:
-
-- [Motion service](/operate/reference/services/slam/): To move machines or components of machines
-- [Frame system service](/operate/reference/services/navigation/): To configure the positions of your components
diff --git a/docs/operate/reference/components/arm/eva.md b/docs/operate/reference/components/arm/eva.md
deleted file mode 100644
index ab206b471e..0000000000
--- a/docs/operate/reference/components/arm/eva.md
+++ /dev/null
@@ -1,50 +0,0 @@
----
-title: "Configure an Eva Arm"
-linkTitle: "eva"
-weight: 34
-type: "docs"
-draft: "true"
-description: "Configure an Eva arm."
-images: ["/icons/components/arm.svg"]
-tags: ["arm", "components"]
-aliases:
- - "/components/arm/eva/"
-toc_hide: true
-# SMEs: Bucket, Motion
----
-
-Configure an `eva` arm to integrate an [Automata Eva](https://automata.tech/products/hardware/about-eva/) robotic arm into your machine.
-
-If you want to test your arm as you configure it, connect it to your machine's computer and turn it on.
-Then, configure the arm:
-
-{{< tabs name="Configure an eva Arm" >}}
-{{% tab name="JSON Template" %}}
-
-```json {class="line-numbers linkable-line-numbers"}
-{
- "name": "",
- "model": "eva",
- "api": "rdk:component:arm",
- "attributes": {}
-}
-```
-
-{{% /tab %}}
-{{< /tabs >}}
-
-{{< readfile "/static/include/components/test-control/arm-control.md" >}}
-
-## Troubleshooting
-
-{{< readfile "/static/include/components/troubleshoot/arm.md" >}}
-
-## Next steps
-
-For more configuration and usage info, see:
-
-{{< cards >}}
-{{% card link="/reference/apis/components/arm/" customTitle="Arm API" noimage="true" %}}
-{{% card link="/operate/modules/configure-modules/" noimage="true" %}}
-{{% card link="/motion-planning/move-an-arm/move-to-pose/" noimage="true" %}}
-{{< /cards >}}
diff --git a/docs/operate/reference/components/arm/fake.md b/docs/operate/reference/components/arm/fake.md
deleted file mode 100644
index 3e4c743340..0000000000
--- a/docs/operate/reference/components/arm/fake.md
+++ /dev/null
@@ -1,94 +0,0 @@
----
-title: "Configure a Fake Arm"
-linkTitle: "fake"
-weight: 34
-type: "docs"
-description: "Configure a fake arm to use for testing."
-images: ["/icons/components/arm.svg"]
-tags: ["arm", "components"]
-aliases:
- - "/components/arm/fake/"
-component_description: "A model used for testing, with no physical hardware."
-toc_hide: true
-# SMEs: Bucket, Motion
----
-
-Configure a `fake` arm to test different models of robotic arms without any physical hardware:
-
-{{< tabs name="Configure a Fake Arm" >}}
-{{% tab name="Config Builder" %}}
-
-Navigate to the **CONFIGURE** tab of your machine's page.
-Click the **+** icon next to your machine part in the left-hand menu and select **Component or service**.
-Select the `arm` type, then select the `fake` model.
-Enter a name or use the suggested name for your arm and click **Create**.
-
-
-
-Fill in the attributes as applicable to your arm, according to the table below.
-
-{{% alert title="Important" color="note" %}}
-
-Only one of these attributes can be supplied for your `fake` arm to work.
-If neither are specified, a fake arm model with 1 degree-of-freedom will be used.
-If both attributes are specified, an error is thrown stating "can only populate either ArmModel or ModelPath - not both".
-
-{{% /alert %}}
-
-{{% /tab %}}
-{{% tab name="JSON Template" %}}
-
-```json {class="line-numbers linkable-line-numbers"}
-{
- "name": "",
- "model": "fake",
- "api": "rdk:component:arm",
- "attributes": {
- "arm-model": "",
- "model-path": ""
- },
- "depends_on": []
-}
-```
-
-{{% /tab %}}
-{{% tab name="JSON Example" %}}
-
-```json {class="line-numbers linkable-line-numbers"}
-{
- "name": "my-fake-arm",
- "model": "fake",
- "api": "rdk:component:arm",
- "attributes": {
- "arm-model": "ur5e"
- },
- "depends_on": []
-}
-```
-
-{{% /tab %}}
-{{< /tabs >}}
-
-The following attributes are available for `fake` arms:
-
-
-| Name | Type | Required? | Description |
-| ---- | ---- | --------- | ----------- |
-| `arm-model` | string | Optional | Model name of the robotic arm model you want your fake arm to act as. See [built-in arm models](../#configuration) for supported model names. |
-| `model-path` | string | Optional | The path to the [kinematic configuration file](/operate/reference/kinematic-chain-config/) of the arm driver you want your fake arm to act as. This path should point to the exact location where the file is located on your computer running `viam-server`. |
-
-{{< readfile "/static/include/components/test-control/arm-control.md" >}}
-
-## Troubleshooting
-
-{{< readfile "/static/include/components/troubleshoot/arm.md" >}}
-
-## Next steps
-
-For more configuration and usage info, see:
-
-{{< cards >}}
-{{% card link="/reference/apis/components/arm/" customTitle="Arm API" noimage="true" %}}
-{{% card link="/operate/modules/configure-modules/" noimage="true" %}}
-{{% card link="/motion-planning/move-an-arm/move-to-pose/" noimage="true" %}}
-{{< /cards >}}
diff --git a/docs/operate/reference/components/arm/yahboom-dofbot.md b/docs/operate/reference/components/arm/yahboom-dofbot.md
deleted file mode 100644
index 2609835212..0000000000
--- a/docs/operate/reference/components/arm/yahboom-dofbot.md
+++ /dev/null
@@ -1,88 +0,0 @@
----
-title: "Configure a Yahboom DOFBOT Arm"
-linkTitle: "yahboom-dofbot"
-weight: 50
-type: "docs"
-draft: "true"
-description: "Configure a Yahboom DOFBOT modular arm."
-images: ["/icons/components/arm.svg"]
-tags: ["arm", "components"]
-aliases:
- - "/components/arm/yahboom-dofbot/"
-toc_hide: true
----
-
-Viam supports the [Yahboom DOFBOT](https://category.yahboom.net/collections/r-robotics-arm) arm as a {{< glossary_tooltip term_id="modular-resource" text="modular resource" >}}.
-You can explore the source code on the [Viam-labs Yahboom GitHub repository](https://github.com/viam-labs/yahboom).
-
-Configure a `dofbot` arm to add it to your machine.
-
-If you want to test your arm as you configure it, connect it to your machine's computer and turn it on.
-Then, configure the arm:
-
-{{< tabs >}}
-{{% tab name="Config Builder" %}}
-
-Navigate to the **CONFIGURE** tab of your machine's page.
-Click the **+** icon next to your machine part in the left-hand menu and select **Component or service**.
-Select the `arm` type, then select the `rand:yahboom:dofbot` modular resource.
-Enter a name or use the suggested name for your arm and click **Create**.
-
-There are no attributes available for this modular arm.
-
-
-
-Edit and fill in the attributes as applicable.
-
-{{% /tab %}}
-{{% tab name="JSON Template" %}}
-
-```json {class="line-numbers linkable-line-numbers"}
-{
- "components": [
- {
- "name": "",
- "model": "dofbot",
- "api": "rdk:component:arm",
- "attributes": {},
- "depends_on": []
- }
- ]
-}
-```
-
-{{% /tab %}}
-{{% tab name="JSON Example" %}}
-
-```json {class="line-numbers linkable-line-numbers"}
-{
- "components": [
- {
- "name": "myarm",
- "model": "yahboom-dofbot",
- "api": "rdk:component:arm",
- "attributes": {},
- "depends_on": []
- }
- ]
-}
-```
-
-{{% /tab %}}
-{{< /tabs >}}
-
-{{< readfile "/static/include/components/test-control/arm-control.md" >}}
-
-## Troubleshooting
-
-{{< readfile "/static/include/components/troubleshoot/arm.md" >}}
-
-## Next steps
-
-For more configuration and usage info, see:
-
-{{< cards >}}
-{{% card link="/reference/apis/components/arm/" customTitle="Arm API" noimage="true" %}}
-{{% card link="/operate/modules/configure-modules/" noimage="true" %}}
-{{% card link="/motion-planning/move-an-arm/move-to-pose/" noimage="true" %}}
-{{< /cards >}}
diff --git a/docs/operate/reference/components/base/_index.md b/docs/operate/reference/components/base/_index.md
deleted file mode 100644
index 390e15aa78..0000000000
--- a/docs/operate/reference/components/base/_index.md
+++ /dev/null
@@ -1,98 +0,0 @@
----
-title: "Base Component"
-linkTitle: "Base"
-weight: 14
-type: "docs"
-no_list: true
-description: "The base component allows you to move a mobile robot without needing to send commands to individual components like motors."
-tags: ["base", "components"]
-icon: true
-images: ["/icons/components/base.svg"]
-modulescript: true
-aliases:
- - "/components/base/"
- - "/micro-rdk/base/"
- - "/build/micro-rdk/base/"
-hide_children: true
-date: "2024-10-21"
-# SMEs: Steve B
----
-
-The base component provides an API for moving all configured drive motors of a mobile robot platform as a whole without needing to send commands to individual motor components.
-
-If you have a mobile robot, use a base component to coordinate the motion of its motor components.
-
-
-
-
-
-## Configuration
-
-Most mobile robots with a base use the following hardware:
-
-- Some actuators to move the base, such as [motors](/operate/reference/components/motor/) attached to wheels or propellers
-- Some sort of chassis to hold everything together
-
-To use a rover or other base, you need to add each component as well as the base to your machine's configuration.
-Go to your machine's **CONFIGURE** page, and add a model that supports your base.
-
-The following list shows the available base models.
-For additional configuration information, click on the model name:
-
-{{< tabs >}}
-{{% tab name="viam-server" %}}
-
-{{}}
-
-{{< readfile "/static/include/create-your-own-mr.md" >}}
-
-{{% /tab %}}
-{{% tab name="Micro-RDK" %}}
-
-
-| Model | Description |
-| ----- | ----------- |
-| [`two_wheeled_base`](two_wheeled_base/) | Mobile robot with two wheels |
-
-{{% readfile "/static/include/micro-create-your-own.md" %}}
-
-{{% /tab %}}
-{{< /tabs >}}
-
-## API
-
-The [base API](/reference/apis/components/base/) supports the following methods:
-
-{{< readfile "/static/include/components/apis/generated/base-table.md" >}}
-
-## Troubleshooting
-
-If your base is not working as expected, follow these steps:
-
-1. Check your machine logs on the **LOGS** tab to check for errors.
-1. Review your base model's documentation to ensure you have configured all required attributes.
-1. Review your configuration for any motors that are components of the base.
- Check that the names of the motor components match the list of motors you configured on the base.
-1. If a motor is spinning in an unexpected direction, try using the `dir_flip` attribute in its config, or try swapping the wires running to the motor to change its direction.
-1. Check that all wires are securely attached to the correct pins.
-1. If you are using a battery to power the base, check that it is adequately charged.
- If the motors are drawing more power than the battery can supply, the single-board computer may be power cycling.
- Consider using a wall power supply for testing purposes to rule out this issue.
-1. Click on the **TEST** panel on the **CONFIGURE** or **CONTROL** tab and test if you can use the base there.
-
-If none of these steps work, reach out to us on the [Community Discord](https://discord.gg/viam) and we will be happy to help.
-
-## Next steps
-
-For general configuration, development, and usage info, see:
-
-{{< cards >}}
-{{% card link="/operate/modules/configure-modules/" noimage="true" %}}
-{{% card link="/tutorials/configure/configure-rover/" noimage="true" %}}
-{{% card link="/tutorials/control/drive-rover/" noimage="true" %}}
-{{< /cards >}}
-
-You can also use the base component with the following services:
-
-- [Navigation service](/operate/reference/services/navigation/): to navigate with GPS
-- [SLAM service](/operate/reference/services/slam/): for mapping
diff --git a/docs/operate/reference/components/base/boat.md b/docs/operate/reference/components/base/boat.md
deleted file mode 100644
index f2149b14bf..0000000000
--- a/docs/operate/reference/components/base/boat.md
+++ /dev/null
@@ -1,88 +0,0 @@
----
-title: "Configure a Boat Base"
-linkTitle: "boat"
-weight: 35
-type: "docs"
-draft: "true"
-description: "Configure a boat base."
-images: ["/icons/components/base.svg"]
-tags: ["base", "components"]
-aliases:
- - "/components/base/boat/"
-toc_hide: true
-# SMEs: Eliot
----
-
-A `boat` base is a model for a mobile robotic boat.
-To configure a `boat` base as a component of your machine, first configure the [board](/operate/reference/components/board/) controlling the base and any [motors](/operate/reference/components/motor/) attached to the base.
-
-{{< tabs name="Configure a Boat Base" >}}
-{{% tab name="Config Builder" %}}
-
-Navigate to the **CONFIGURE** tab of your machine's page.
-Click the **+** icon next to your machine part in the left-hand menu and select **Component or service**.
-Select the `base` type, then select the `boat` model.
-Enter a name or use the suggested name for your base and click **Create**.
-
-Edit and fill in the attributes as applicable.
-
-{{% /tab %}}
-{{% tab name="JSON Template" %}}
-
-```json {class="line-numbers linkable-line-numbers"}
-{
- "components": [
- {
- "name": "base",
- "model": "boat",
- "api": "rdk:component:base",
- "attributes": {
- "drive_mode": "",
- "serial_path": ""
- },
- "depends_on": []
- }
-}
-```
-
-{{% /tab %}}
-{{< /tabs >}}
-
-The following attributes are available for `boat` bases:
-
-
-| Name | Type | Required? | Description |
-| ---- | ---- | --------- | ----------- |
-| `length_mm` | int | **Required** | Length of the base in millimeters. In other words, the distance between the approximate centers of the right and left wheels. Can be an approximation. |
-| `width_mm` | int | **Required** | Width of the base in millimeters. In other words, the distance between the approximate centers of the right and left motors. Can be an approximation. |
-| `IMU` | string | **Required** | Name of the [Inertial Measurement Unit](/reference/apis/components/movement-sensor/#imu-configuration) in the boat. |
-| `Motors` | string[] | **Required** | JSON struct containing the configuration attributes for each motor attached to the boat. |
-
-Each [motor](/operate/reference/components/motor/) inside of `Motors` has the following attributes available:
-
-
-| Name | Type | Required? | Description |
-| ---- | ---- | --------- | ----------- |
-| `Name` | string | **Required** | Name of the motor. |
-| `x_offset_mm` | int | **Required** | |
-| `y_offset_mm` | int | **Required** | |
-| `angle_degs` | int | **Required** | |
-| `Weight` | int | **Required** | |
-
-## Test the base
-
-{{< readfile "/static/include/components/test-control/base-control.md" >}}
-
-## Troubleshooting
-
-{{< readfile "/static/include/components/troubleshoot/base.md" >}}
-
-## Next steps
-
-For more configuration and usage info, see:
-
-{{< cards >}}
-{{% card link="/reference/apis/components/base/" customTitle="Base API" noimage="true" %}}
-{{% card link="/tutorials/configure/configure-rover/" noimage="true" %}}
-{{% card link="/tutorials/control/drive-rover/" noimage="true" %}}
-{{< /cards >}}
diff --git a/docs/operate/reference/components/base/fake.md b/docs/operate/reference/components/base/fake.md
deleted file mode 100644
index cb60e84719..0000000000
--- a/docs/operate/reference/components/base/fake.md
+++ /dev/null
@@ -1,62 +0,0 @@
----
-title: "Configure a Fake Base"
-linkTitle: "fake"
-weight: 20
-type: "docs"
-description: "Configure a fake base to use for testing without physical hardware."
-images: ["/icons/components/base.svg"]
-tags: ["base", "components"]
-aliases:
- - "/components/base/fake/"
-component_description: "A model used for testing, with no physical hardware."
-toc_hide: true
-# SMEs: Steve B
----
-
-Configure a `fake` base to test implementing a base component on your machine without any physical hardware:
-
-{{< tabs name="Configure a Fake Base" >}}
-{{% tab name="Config Builder" %}}
-
-Navigate to the **CONFIGURE** tab of your machine's page.
-Click the **+** icon next to your machine part in the left-hand menu and select **Component or service**.
-Select the `base` type, then select the `fake` model.
-Enter a name or use the suggested name for your base and click **Create**.
-
-{{< imgproc src="/components/base/fake-base-ui-config.png" alt="An example configuration for a fake base." resize="1200x" style="width: 900px" class="shadow" >}}
-
-{{% /tab %}}
-{{% tab name="JSON Template" %}}
-
-```json {class="line-numbers linkable-line-numbers"}
-{
- "name": "",
- "api": "rdk:component:base",
- "model": "fake",
- "attributes": {}
-}
-```
-
-{{% /tab %}}
-{{< /tabs >}}
-
-No attributes are available for `fake` bases.
-See [GitHub](https://github.com/viamrobotics/rdk/blob/main/components/base/fake/fake.go) for API call return specifications.
-
-## Test the base
-
-{{< readfile "/static/include/components/test-control/base-control.md" >}}
-
-## Troubleshooting
-
-{{< readfile "/static/include/components/troubleshoot/base.md" >}}
-
-## Next steps
-
-For more configuration and usage info, see:
-
-{{< cards >}}
-{{% card link="/reference/apis/components/base/" customTitle="Base API" noimage="true" %}}
-{{% card link="/tutorials/configure/configure-rover/" noimage="true" %}}
-{{% card link="/tutorials/control/drive-rover/" noimage="true" %}}
-{{< /cards >}}
diff --git a/docs/operate/reference/components/base/sensor-controlled.md b/docs/operate/reference/components/base/sensor-controlled.md
deleted file mode 100644
index 31665728f8..0000000000
--- a/docs/operate/reference/components/base/sensor-controlled.md
+++ /dev/null
@@ -1,126 +0,0 @@
----
-title: "Configure a Sensor-Controlled Base"
-linkTitle: "sensor-controlled"
-weight: 40
-type: "docs"
-description: "Configure a sensor-controlled base, a robotic base with feedback control from a movement sensor."
-images: ["/icons/components/base.svg"]
-tags: ["base", "components"]
-aliases:
- - "/components/base/sensor-controlled/"
-component_description: "Wrap other base models and add feedback control using a movement sensor."
-toc_hide: true
-# SMEs: Rand H., Martha J.
----
-
-A `sensor-controlled` base supports a robotic base with feedback control from a movement sensor.
-
-{{% alert title="Requirements" color="note" %}}
-In order to use feedback control, you must provide a movement sensor that implements [AngularVelocity()](/reference/apis/components/movement-sensor/#getangularvelocity) and [LinearVelocity()](/reference/apis/components/movement-sensor/#getlinearvelocity). This will enable feedback control for [SetVelocity()](/reference/apis/components/base/#setvelocity).
-
-In order to use feedback control for [Spin()](/reference/apis/components/base/#spin), you must also provide a movement sensor that implements [Orientation()](/reference/apis/components/movement-sensor/#getorientation).
-
-In order to use feedback control for [MoveStraight()](/reference/apis/components/base/#spin), you must also provide a movement sensor that implements [Position()](/reference/apis/components/movement-sensor/#getposition).
-Additionally, heading feedback control while moving straight can be used by providing a movement sensor that implements [Orientation()](/reference/apis/components/movement-sensor/#getorientation) or [CompassHeading()](/reference/apis/components/movement-sensor/#getcompassheading).
-{{% /alert %}}
-
-To configure a `sensor-controlled` base as a component of your machine, first configure the [model of base](/operate/reference/components/base/) you want to wrap with feedback control and each required [movement sensor](/operate/reference/components/movement-sensor/).
-To see what models of movement sensor report which feedback, reference the appropriate column in [Movement Sensor API](/reference/apis/components/movement-sensor/#api).
-
-Configure a `sensor-controlled` base as follows:
-
-{{< tabs name="Configure a Sensor-Controlled Base" >}}
-{{% tab name="Config Builder" %}}
-
-Navigate to the **CONFIGURE** tab of your machine's page.
-Click the **+** icon next to your machine part in the left-hand menu and select **Component or service**.
-Select the `base` type, then select the `sensor-controlled` model.
-Enter a name or use the suggested name for your base and click **Create**.
-
-{{< imgproc src="/components/base/sensor-controlled-base-ui-config.png" alt="An example configuration for a sensor-controlled base" resize="1200x" style="width: 600x" class="shadow" >}}
-
-Edit and fill in the attributes as applicable.
-
-{{% /tab %}}
-{{% tab name="JSON Template" %}}
-
-```json {class="line-numbers linkable-line-numbers"}
-{
- "components": [
- { ... INSERT MOVEMENT SENSOR CONFIGURATION },
- { ... INSERT BASE CONFIGURATION },
- {
- "name": "my-sensor-controlled-base",
- "model": "sensor-controlled",
- "api": "rdk:component:base",
- "attributes": {
- "movement_sensor": [
- "",
- ""
- ],
- "base": ""
- },
- "depends_on": []
- }
- ]
-}
-```
-
-{{% /tab %}}
-{{< /tabs >}}
-
-The following attributes are available for `sensor-controlled` bases:
-
-
-| Name | Type | Required? | Description |
-| ---- | ---- | --------- | ----------- |
-| `movement_sensor` | array | **Required** | Array with the `name`s of any movement sensors on your base you want to gather feedback from. The driver will select the first movement sensor providing appropriate feedback for either the `SetVelocity()` or the `Spin()` endpoint. If your sensor has an adjustable frequency or period, set the frequency to something greater than or equal to the `control_frequency_hz`. A higher frequency will generally result in more stable behavior because the base control loop that adjusts the machine's behavior runs more frequently. |
-| `base` | string | **Required** | String with the `name` of the base you want to wrap with sensor control. |
-| `control_parameters` | object | Optional | A JSON object containing the coefficients for the proportional, integral, and derivative terms for linear and angular velocity. If you want these values to be auto-tuned, you can set all values to 0: `[ { "type": "linear_velocity", "p": 0, "i": 0, "d": 0 }, { "type": "angular_velocity", "p": 0, "i": 0, "d": 0 } ]`, and `viam-server` will auto-tune and log the calculated values. Tuning takes several seconds and spins the motors. Copy the values from the logs and add them to the configuration once tuned for the values to take effect. If you need to auto-tune multiple controlled components that depend on the same hardware, such as a sensor controlled base and one of the motors on the base, run the auto-tuning process one component at a time. For more information see [Feedback control](#feedback-control). |
-| `control_frequency_hz` | float | Optional | Adjusts the frequency that the base control loop runs at. A higher frequency will generally result in more stable behavior because the base control loop that adjusts the machine's behavior runs more frequently, provided the movement sensors can support the higher frequency. The default base control loop frequency is 10Hz. |
-
-## Feedback control
-
-### SetVelocity
-
-{{< readfile "/static/include/components/base-sensor.md" >}}
-
-### Spin
-
-When the `control_parameters` attribute is set, `Spin` implements a form of feedback control that polls the provided movement sensor and corrects any error between the desired angular velocity and the actual angular velocity using a PID control loop. `Spin` also monitors the angular distance traveled and stops the base when the goal angle is reached.
-
-### MoveStraight
-
-When `control_parameters` is set, `MoveStraight` calculates the required velocity to reach the desired velocity and distance. It then polls the provided velocity movement sensor and corrects any error between this calculated velocity and the actual velocity using a PID control loop. `MoveStraight` also monitors the position and stops the base when the goal distance is reached. If a compass heading movement sensor is provided, `MoveStraight` will attempt to keep the heading of the base fixed in the original direction it was faced at the beginning of the `MoveStraight` call.
-
-## Test the base
-
-{{< readfile "/static/include/components/test-control/base-control.md" >}}
-
-The following base control API methods are available on a `sensor-controlled` base:
-
-- [SetVelocity()](/reference/apis/components/base/#setvelocity): available if base is configured to receive angular and linear velocity feedback.
-- [Spin()](/reference/apis/components/base/#spin): available if base is configured to receive orientation feedback.
-- [MoveStraight()](/reference/apis/components/base/#movestraight): available if base is configured to receive position feedback.
-
-For example, a rover using `sensor-controlled` base following both an [angular](/reference/apis/components/base/#spin) and [linear](/reference/apis/components/base/#movestraight) velocity command:
-
-{{}}
-
-The position, orientation, and linear and angular velocity of the rover changing as it moves, as measured by a [movement sensor](/operate/reference/components/movement-sensor/):
-
-{{}}
-
-## Troubleshooting
-
-{{< readfile "/static/include/components/troubleshoot/base.md" >}}
-
-## Next steps
-
-For more configuration and usage info, see:
-
-{{< cards >}}
-{{% card link="/reference/apis/components/base/" customTitle="Base API" noimage="true" %}}
-{{% card link="/tutorials/configure/configure-rover/" noimage="true" %}}
-{{% card link="/tutorials/control/drive-rover/" noimage="true" %}}
-{{< /cards >}}
diff --git a/docs/operate/reference/components/base/two_wheeled_base.md b/docs/operate/reference/components/base/two_wheeled_base.md
deleted file mode 100644
index a2c7591677..0000000000
--- a/docs/operate/reference/components/base/two_wheeled_base.md
+++ /dev/null
@@ -1,97 +0,0 @@
----
-title: "Configure a Two-Wheeled Base (Micro-RDK)"
-linkTitle: "two_wheeled_base (Micro-RDK)"
-weight: 30
-type: "docs"
-description: "Configure and wire a two-wheeled base with a microcontroller."
-images: ["/icons/components/base.svg"]
-tags: ["base", "components"]
-aliases:
- - /micro-rdk/base/esp32_wheeled_base/
- - /build/micro-rdk/base/esp32_wheeled_base/
- - /build/micro-rdk/base/two_wheeled_base/
- - /components/base/two_wheeled_base/
-micrordk_component: true
-toc_hide: true
-# SMEs: Gautham V.
----
-
-A `two_wheeled_base` base supports mobile robotic bases with drive motors on both sides (differential steering).
-Only bases with two drive wheels are supported by this `viam-micro-server` model.
-
-{{< alert title="Info" color="info" >}}
-
-The`two_wheeled_base` base model is not currently available when configuring your machine using **Builder** mode.
-
-{{< /alert >}}
-
-If you want to test your base as you configure it, physically assemble the base, connect it to your machine's computer, and turn it on.
-Then, configure a `two_wheeled_base` base as follows:
-
-{{< tabs name="Configure a Wheeled Base" >}}
-{{% tab name="JSON Template" %}}
-
-```json {class="line-numbers linkable-line-numbers"}
-{
- "components": [
- {
- "name": "",
- "model": "two_wheeled_base",
- "api": "rdk:component:base",
- "attributes": {
- "left": "",
- "right": ""
- }
- }
- ]
-}
-```
-
-{{% /tab %}}
-{{% tab name="JSON Example" %}}
-
-```json {class="line-numbers linkable-line-numbers"}
-{
- "components": [
- {
- "name": "my-wheeled-base",
- "model": "two_wheeled_base",
- "api": "rdk:component:base",
- "attributes": {
- "left": "leftm",
- "right": "rightm"
- },
- "depends_on": []
- }, ...
- ]
-}
-```
-
-{{% /tab %}}
-{{< /tabs >}}
-
-The following attributes are available for `two_wheeled_base` bases:
-
-
-| Name | Type | Required? | Description |
-| ---- | ---- | --------- | ----------- |
-| `left` | string | **Required** | The `name` of a drive motor on the left side of the base. |
-| `right` | string | **Required** | The `name` of a drive motor on the right side of the base. |
-
-## Test the base
-
-{{< readfile "/static/include/components/test-control/base-control.md" >}}
-
-## Troubleshooting
-
-{{< readfile "/static/include/components/troubleshoot/base.md" >}}
-
-## Next steps
-
-For more configuration and usage info, see:
-
-{{< cards >}}
-{{% card link="/reference/apis/components/base/" customTitle="Base API" noimage="true" %}}
-{{% card link="/tutorials/configure/configure-rover/" noimage="true" %}}
-{{% card link="/tutorials/control/drive-rover/" noimage="true" %}}
-{{< /cards >}}
diff --git a/docs/operate/reference/components/base/wheeled.md b/docs/operate/reference/components/base/wheeled.md
deleted file mode 100644
index 18cd49495e..0000000000
--- a/docs/operate/reference/components/base/wheeled.md
+++ /dev/null
@@ -1,168 +0,0 @@
----
-title: "Configure a Wheeled Base"
-linkTitle: "wheeled"
-weight: 30
-type: "docs"
-description: "Configure and wire a wheeled base."
-images: ["/icons/components/base.svg"]
-tags: ["base", "components"]
-aliases:
- - "/components/base/wheeled/"
-component_description: "Supports mobile wheeled robotic bases with motors on both sides for differential steering."
-toc_hide: true
-# SMEs: Steve B
----
-
-A `wheeled` base supports mobile robotic bases with motors on both sides (differential steering).
-To configure a `wheeled` base as a component of your machine, first configure the [board](/operate/reference/components/board/) controlling the base and any [motors](/operate/reference/components/motor/) attached to the base.
-
-If you want to test your base as you configure it, physically assemble the base, connect it to your machine's computer, and turn it on.
-Then, configure a `wheeled` base as follows:
-
-{{< tabs name="Configure a Wheeled Base" >}}
-{{% tab name="Config Builder" %}}
-
-Navigate to the **CONFIGURE** tab of your machine's page.
-Click the **+** icon next to your machine part in the left-hand menu and select **Component or service**.
-Select the `base` type, then select the `wheeled` model.
-Enter a name or use the suggested name for your base and click **Create**.
-
-{{< imgproc src="/components/base/wheeled-base-ui-config.png" alt="An example configuration for a wheeled base, with Attributes & Depends On dropdowns and the option to add a frame." resize="1200x" style="width: 900px" class="shadow" >}}
-
-Select the motors attached to the base as your **right** and **left** motors.
-Edit and fill in the attributes as applicable.
-
-{{% /tab %}}
-{{% tab name="JSON Template" %}}
-
-```json {class="line-numbers linkable-line-numbers"}
-{
- "components": [
- {
- ...
- },
- {
- ...
- },
- {
- ...
- },
- {
- "name": "",
- "model": "wheeled",
- "api": "rdk:component:base",
- "attributes": {
- "left": [
- "" //
- ],
- "right": [
- "" //
- ],
- "wheel_circumference_mm": ,
- "width_mm":
- },
- "depends_on": []
- }
- ]
-}
-```
-
-{{% /tab %}}
-{{% tab name="JSON Example" %}}
-
-```json
-{
- "components": [
- {
- "name": "my-pi",
- "model": "pi",
- "api": "rdk:component:board",
- "attributes": {},
- "depends_on": []
- },
- {
- "name": "rightm",
- "model": "gpio",
- "api": "rdk:component:motor",
- "attributes": {
- "pins": {
- "dir": "16",
- "pwm": "15"
- },
- "board": "my-pi"
- },
- "depends_on": []
- },
- {
- "name": "leftm",
- "model": "gpio",
- "api": "rdk:component:motor",
- "attributes": {
- "pins": {
- "dir": "13",
- "pwm": "11"
- },
- "board": "my-pi"
- },
- "depends_on": []
- },
- {
- "name": "my-wheeled-base",
- "model": "wheeled",
- "api": "rdk:component:base",
- "attributes": {
- "width_mm": 195,
- "wheel_circumference_mm": 183,
- "left": ["leftm"],
- "right": ["rightm"]
- },
- "depends_on": []
- }
- ]
-}
-```
-
-{{% /tab %}}
-{{% tab name="Annotated JSON" %}}
-
-{{< imgproc src="/components/base/base-json.png" alt="JSON configuration file for a wheeled base with annotations explaining some of the attributes." resize="600x" class="shadow" >}}
-
-{{% /tab %}}
-{{< /tabs >}}
-
-The following attributes are available for `wheeled` bases:
-
-
-| Name | Type | Required? | Description |
-| ---- | ---- | --------- | ----------- |
-| `left` | array | **Required** | Array with the `name` of any drive motors on the left side of the base. |
-| `right` | array | **Required** | Array with the `name` of any drive motors on the right side of the base. |
-| `wheel_circumference_mm` | int | **Required** | The outermost circumference of the drive wheels in millimeters. Used for odometry. Can be an approximation. |
-| `width_mm` | int | **Required** | Width of the base in millimeters. In other words, the distance between the approximate centers of the right and left wheels. Can be an approximation. |
-| `spin_slip_factor` | float | Optional | Can be used in steering calculations to correct for slippage between the wheels and the floor. If used, calibrated by the user. |
-
-## Wire a `wheeled` base
-
-An example wiring diagram for a base with one motor on each side:
-
-
-
-Note that your base's wiring will vary depending on your choice of board, motors, motor drivers, and power supply.
-
-## Test the base
-
-{{< readfile "/static/include/components/test-control/base-control.md" >}}
-
-## Troubleshooting
-
-{{< readfile "/static/include/components/troubleshoot/base.md" >}}
-
-## Next steps
-
-For more configuration and usage info, see:
-
-{{< cards >}}
-{{% card link="/reference/apis/components/base/" customTitle="Base API" noimage="true" %}}
-{{% card link="/tutorials/configure/configure-rover/" noimage="true" %}}
-{{% card link="/tutorials/control/drive-rover/" noimage="true" %}}
-{{< /cards >}}
diff --git a/docs/operate/reference/components/board/_index.md b/docs/operate/reference/components/board/_index.md
deleted file mode 100644
index 0741054b46..0000000000
--- a/docs/operate/reference/components/board/_index.md
+++ /dev/null
@@ -1,105 +0,0 @@
----
-title: "Board Component"
-linkTitle: "Board"
-childTitleEndOverwrite: "Board Component"
-weight: 17
-type: "docs"
-no_list: true
-description: "The signal wire hub of a machine, with GPIO pins for transmitting signals between the machine's computer and its other components."
-tags: ["board", "components"]
-icon: true
-images: ["/icons/components/board.svg"]
-modulescript: true
-aliases:
- - "/components/board/"
- - "/micro-rdk/board/"
- - "/build/micro-rdk/board/"
-hide_children: true
-date: "2024-10-21"
-# SMEs: Gautham, Rand
----
-
-The board component provides an API for setting GPIO pins to high or low, setting PWM, and working with analog and digital interrupts.
-
-If you have GPIO pins you wish to control, use a board component.
-
-Your GPIO pins can be present as:
-
-- The GPIO pins on a single-board computer (SBC).
-- A GPIO peripheral device that must connect to an external computer.
-- A PWM peripheral device that must connect to an SBC that has a CPU and GPIO pins.
-
-In other words, the board of a machine is its signal wire hub.
-Signaling controls the flow of electricity to these pins to change their state between "high" (active) and "low" (inactive), and to send [digital signals](https://en.wikipedia.org/wiki/Digital_signal) to and from other hardware.
-
-{{< alert title="Running viam-server" color="note" >}}
-
-The board component allows you to use the pins on your board.
-If there is no board model for your board:
-
-- you can still run `viam-server` if your board [supports it](/operate/install/setup/)
-- you can still access USB ports
-
-{{< /alert >}}
-
-## Configuration
-
-To use GPIO pins, you need to add a board component to your machine's configuration.
-Go to your machine's **CONFIGURE** page, and add a model that supports your board.
-
-The following list shows the available board models.
-If your board is not among them, you may be able to use the pins on your board with an experimental [periph.io](https://periph.io/)-based [modular component](https://github.com/viam-labs/periph_board).
-This works for boards such as the [RockPi S](https://wiki.radxa.com/RockpiS).
-
-For additional configuration information, click on the model name:
-
-{{< tabs >}}
-{{% tab name="viam-server" %}}
-
-{{}}
-
-{{< alert title="Add support for other models" color="tip" >}}
-If none of the existing models fit your use case, you can [create a {{< glossary_tooltip term_id="modular-resource" text="modular resource" >}}](/operate/modules/write-a-driver-module/) to add support for it.
-
-For Linux boards like the Odroid C4, Pumpkin, or, Banana Pi, you can also use the [`customlinux` board](https://github.com/viam-modules/customlinux/blob/main/README.md).
-{{< /alert >}}
-
-{{% /tab %}}
-{{% tab name="Micro-RDK" %}}
-
-
-| Model | Description |
-| ----- | ----------- |
-| [`esp32`](esp32/) | An ESP32 microcontroller |
-
-{{% readfile "/static/include/micro-create-your-own.md" %}}
-
-{{% /tab %}}
-{{< /tabs >}}
-
-## API
-
-The [board API](/reference/apis/components/board/) supports the following methods:
-
-{{< readfile "/static/include/components/apis/generated/board-table.md" >}}
-
-## Troubleshooting
-
-If your board is not working as expected, follow these steps:
-
-1. Check your machine logs on the **LOGS** tab to check for errors.
-1. Review your board model's documentation to ensure you have configured all required attributes.
-1. Check that all wires are securely connected.
-1. Click on the **TEST** panel on the **CONFIGURE** or **CONTROL** tab and test if you can use the board there.
-
-If none of these steps work, reach out to us on the [Community Discord](https://discord.gg/viam) and we will be happy to help.
-
-## Next steps
-
-For general configuration and development info, see:
-
-{{< cards >}}
-{{% card link="/operate/modules/configure-modules/" noimage="true" %}}
-{{% card link="/operate/control/web-app/" noimage="true" %}}
-{{% card link="/tutorials/get-started/blink-an-led" noimage="true" %}}
-{{< /cards >}}
diff --git a/docs/operate/reference/components/board/esp32.md b/docs/operate/reference/components/board/esp32.md
deleted file mode 100644
index b5a097686d..0000000000
--- a/docs/operate/reference/components/board/esp32.md
+++ /dev/null
@@ -1,200 +0,0 @@
----
-title: "Configure an ESP32 Board (Micro-RDK)"
-linkTitle: "esp32 (Micro-RDK)"
-weight: 20
-type: "docs"
-description: "Configure an ESP32 board running viam-micro-server."
-images: ["/installation/thumbnails/esp32-espressif.png"]
-imageAlt: "E S P 32 - espressif"
-tags: ["board", "components"]
-aliases:
- - /micro-rdk/board/esp32/
- - /build/micro-rdk/board/esp32/
- - /components/board/esp32/
-micrordk_component: true
-toc_hide: true
-# SMEs: Gautham, Nico, Andrew
----
-
-{{% alert title="REQUIREMENTS" color="caution" %}}
-
-Follow the [setup guide](/operate/install/setup/) to install and run `viam-micro-server` before configuring an `esp32` board.
-
-Viam recommends purchasing the ESP32 with a development board.
-The following ESP32 microcontrollers are supported:
-
-- ESP-32-WROVER Series
-
-Your microcontroller should have at least the following resources available to work with `viam-micro-server`:
-
-- 2 Cores + 384kB SRAM + 2MB PSRAM + 8MB Flash
-
-{{% /alert %}}
-
-To test your board as you configure it, power it on.
-If you plan to connect hardware to the board's pins, connect the hardware while it's powered off.
-To configure an `esp32` board, navigate to the **CONFIGURE** tab of your machine's page and select **JSON** mode.
-
-{{< alert title="Info" color="info" >}}
-
-The`esp32` [board](/operate/reference/components/board/) model is not currently available when configuring your machine using **Builder** mode.
-
-{{< /alert >}}
-
-Copy the following JSON template and paste it into your configuration inside the `"components"` array:
-
-{{< tabs name="Configure an esp32 Board" >}}
-{{% tab name="JSON Template"%}}
-
-```json {class="line-numbers linkable-line-numbers"}
-{
- "name": "",
- "model": "esp32",
- "api": "rdk:component:board",
- "attributes": {
- "pins": [
-
- ],
- "analogs": [
- {
- "pin": ,
- "name": ""
- }
- ],
- "digital_interrupts" : [
- {
- "pin":
- }
- ]
- },
- "depends_on": []
-}
-```
-
-{{% /tab %}}
-{{% tab name="JSON Example" %}}
-
-```json {class="line-numbers linkable-line-numbers"}
-{
- "name": "board",
- "model": "esp32",
- "api": "rdk:component:board",
- "attributes": {
- "pins": [15, 34],
- "analogs": [
- {
- "pin": "34",
- "name": "sensor"
- }
- ],
- "digital_interrupts": [
- {
- "pin": 4
- }
- ]
- },
- "depends_on": []
-}
-```
-
-{{% /tab %}}
-{{< /tabs >}}
-
-Edit and fill in the attributes as applicable.
-Click the **Save** button in the top right corner of the page.
-
-The following attributes are available for `esp32` boards:
-
-
-| Name | Type | Required? | Description |
-| ---- | ---- | --------- | ----------- |
-| `analogs` | array | Optional | Attributes of any pins that can be used as analog-to-digital converter (ADC) inputs. See [configuration info](#analogs). |
-| `i2cs` | array | Optional | Any Inter-Integrated Circuit (I2C) pins' bus index and name. See [configuration info](#i2cs). |
-| `digital_interrupts` | array | Optional | Any digital interrupts' GPIO number. See [configuration info](#digital_interrupts). |
-| `pins` | array | Optional | The GPIO number of any GPIO pins you wish to use as input/output with the [board API](/reference/apis/components/board/#api). |
-
-Any pin not specified in either `"pins"` or `"digital_interrupts"` cannot be interacted with through the [board API](/reference/apis/components/board/#api).
-Interaction with digital interrupts is only supported with the [board API](/reference/apis/components/board/#api); these digital interrupts cannot be used as software interrupts in driver implementations.
-
-## `analogs`
-
-The following properties are available for `analogs`:
-
-
-| Name | Type | Required? | Description |
-| ---- | ---- | --------- | ----------- |
-| `name` | string | **Required** | Your name for the analog reader. |
-| `pin`| integer | **Required** | The GPIO number of the ADC's connection pin, wired to the board. |
-
-### `i2cs`
-
-The following properties are available for `i2cs`:
-
-
-| Name | Type | Required? | Description |
-| ---- | ---- | --------- | ----------- |
-|`name`| string| **Required** | `name` of the I2C bus. |
-|`bus`| string | **Required** | The index of the I2C bus. Must be either `i2c0` or `i2c1`. |
-|`data_pin`| integer | Optional | The GPIO number of the data pin. Default: `11`. |
-|`clock_pin`| integer | Optional | The GPIO number of the clock pin. Default: `6`. |
-|`baudrate_hz`| integer | Optional | The baudrate in HZ of this I2C bus. Default: `1000000` |
-|`timeout_ns`| integer | Optional | Timeout period for this I2C bus in nanoseconds. Default: `0` |
-
-### `digital_interrupts`
-
-The following properties are available for `digital_interrupts`:
-
-
-| Name | Type | Required? | Description |
-| ---- | ---- | --------- | ----------- |
-|`pin`| integer | **Required** | The GPIO number of the board's GPIO pin that you wish to configure the digital interrupt for. |
-
-### PWM signals on `esp32` pins
-
-You can set PWM frequencies with Viam through the [`GPIOPin` API](/reference/apis/components/board/#api).
-Refer to the [Espressif documentation for valid frequencies and duty resolutions](https://docs.espressif.com/projects/esp-idf/en/v5.4/esp32/api-reference/peripherals/ledc.html#supported-range-of-frequency-and-duty-resolutions).
-A configured `esp32` board can support a maximum of four different PWM frequencies simultaneously, as the boards only have four available timers.
-
-For example:
-
-| Pin | PWM Frequency (Hz) |
-| --- | ------------------ |
-| 12 | 2000 |
-| 25 | 3000 |
-| 26 | 5000 |
-| 32 | 6000 |
-
-At this point, if you want to add another PWM signal, you must do the following:
-
-1. Set the PWM frequency before the duty cycle.
-2. Set the PWM frequency to one of the above previously set frequencies.
-
-For example, you can set pin 33 to 2000 Hz:
-
-| Pin | PWM Frequency (Hz) |
-| ------ | ------------------ |
-| 12, 33 | 2000 |
-| 25 | 3000 |
-| 26 | 5000 |
-| 32 | 6000 |
-
-Then, follow these requirements to change the PWM frequencies of a pin:
-
-1. If no other pins have a signal of the same frequency (for example, pins 25, 26, and 32), you can freely change their PWM frequency.
-2. If one or more pins are sharing the old frequency (for example, pins 12 and 33):
- 1. If there are less than 4 active frequencies, you can change the PWM frequency freely because there will be a timer available.
- 2. If there are already 4 active frequencies, changing the PWM frequency of the pin will raise an error because there are no timers available. Free a timer by setting the PWM frequencies of all of the pins to 0.
-
-## Troubleshooting
-
-{{< readfile "/static/include/components/troubleshoot/board.md" >}}
-
-## Next steps
-
-For more configuration and usage info, see:
-
-{{< cards >}}
-{{% card link="/reference/apis/components/board/" customTitle="Board API" noimage="true" %}}
-{{% card link="/operate/control/web-app/" noimage="true" %}}
-{{% card link="/tutorials/get-started/blink-an-led/" noimage="true" %}}
-{{< /cards >}}
diff --git a/docs/operate/reference/components/board/fake.md b/docs/operate/reference/components/board/fake.md
deleted file mode 100644
index 35a6fc334b..0000000000
--- a/docs/operate/reference/components/board/fake.md
+++ /dev/null
@@ -1,87 +0,0 @@
----
-title: "Configure a Fake Board"
-linkTitle: "fake"
-weight: 20
-type: "docs"
-description: "Configure a fake board."
-images: ["/icons/components/board.svg"]
-tags: ["board", "components"]
-aliases:
- - "/components/board/fake/"
-component_description: "A model used for testing, with no physical hardware."
-toc_hide: true
-# SMEs: Gautham, Rand
----
-
-The `fake` board returns incrementing values for digital interrupt ticks and analogs.
-
-Configure a `fake` board to test integrating a board into your machine without physical hardware:
-
-{{< tabs name="Configure an fake Board" >}}
-{{% tab name="Config Builder" %}}
-
-Navigate to the **CONFIGURE** tab of your machine's page.
-Click the **+** icon next to your machine part in the left-hand menu and select **Component or service**.
-Select the `board` type, then select the `fake` model.
-Enter a name or use the suggested name for your board and click **Create**.
-
-
-
-Edit the attributes as applicable to your board, according to the table below.
-
-{{% /tab %}}
-{{% tab name="JSON Template" %}}
-
-```json {class="line-numbers linkable-line-numbers"}
-{
- "components": [
- {
- "name": "",
- "model": "fake",
- "api": "rdk:component:board",
- "attributes": {
- "fail_new":
- },
- "depends_on": []
- }
- ]
-}
-```
-
-{{% /tab %}}
-{{< /tabs >}}
-
-The following attributes are available for `fake` boards:
-
-
-| Name | Type | Required? | Description |
-| ---- | ---- | --------- | ----------- |
-| `fail_new` | bool | **Required** | If the fake board should raise an error at machine start-up. |
-| `analogs` | object | Optional | Attributes of any pins that can be used as Analog-to-Digital Converter (ADC) inputs. See [configuration info](#analogs). |
-| `digital_interrupts` | object | Optional | Pin and name of any digital interrupts. See [configuration info](#digital_interrupts). |
-
-## Attribute configuration
-
-Configuring these attributes on your board allows you to integrate [analog-to-digital converters](#analogs) and [digital interrupts](#digital_interrupts) into your machine.
-
-### `analogs`
-
-{{< readfile "/static/include/components/board/board-analogs.md" >}}
-
-### `digital_interrupts`
-
-{{< readfile "/static/include/components/board/board-digital-interrupts.md" >}}
-
-## Troubleshooting
-
-{{< readfile "/static/include/components/troubleshoot/board.md" >}}
-
-## Next steps
-
-For more configuration and usage info, see:
-
-{{< cards >}}
-{{% card link="/reference/apis/components/board/" customTitle="Board API" noimage="true" %}}
-{{% card link="/operate/control/web-app/" noimage="true" %}}
-{{% card link="/tutorials/get-started/blink-an-led/" noimage="true" %}}
-{{< /cards >}}
diff --git a/docs/operate/reference/components/button/_index.md b/docs/operate/reference/components/button/_index.md
deleted file mode 100644
index 333bbf185c..0000000000
--- a/docs/operate/reference/components/button/_index.md
+++ /dev/null
@@ -1,69 +0,0 @@
----
-title: "Button Component"
-linkTitle: "Button"
-childTitleEndOverwrite: "Button Component"
-weight: 20
-type: "docs"
-description: "A physical button on a machine."
-tags: ["button", "components"]
-icon: true # this should be used when the image is an icon, it will adjust the sizing and object-fit
-images: ["/icons/components/button.svg"]
-no_list: true
-modulescript: true
-date: "2025-02-20"
-# SMEs:
----
-
-The button component provides an API for getting presses of a physical button on a machine.
-
-## Configuration
-
-To use your button component, check whether one of the following models supports it.
-
-For configuration information, click on the model name:
-
-{{< tabs >}}
-{{% tab name="viam-server" %}}
-
-{{}}
-
-{{< readfile "/static/include/create-your-own-mr.md" >}}
-
-{{% /tab %}}
-{{% tab name="Micro-RDK" %}}
-
-{{< alert title="Support Notice" color="note" >}}
-
-There is currently no support for this component compatible with the Micro-RDK.
-
-{{< /alert >}}
-
-{{% /tab %}}
-{{< /tabs >}}
-
-## API
-
-The [button API](/reference/apis/components/button/) supports the following methods:
-
-{{< readfile "/static/include/components/apis/generated/button-table.md" >}}
-
-## Troubleshooting
-
-If your button is not working as expected, follow these steps:
-
-1. Check your machine logs on the **LOGS** tab to check for errors.
-1. Review your button model's documentation to ensure you have configured all required attributes.
-1. Click on the **TEST** panel on the **CONFIGURE** or **CONTROL** tab and test if you can use the button there.
-1. Disconnect and reconnect your button.
-
-If none of these steps work, reach out to us on the [Community Discord](https://discord.gg/viam) and we will be happy to help.
-
-## Next steps
-
-For general configuration, development, and usage info, see:
-
-{{< cards >}}
-{{% card link="/operate/modules/configure-modules/" noimage="true" %}}
-{{% card link="/operate/control/web-app/" noimage="true" %}}
-{{% card link="/tutorials/services/plan-motion-with-arm-gripper" noimage="true" %}}
-{{< /cards >}}
diff --git a/docs/operate/reference/components/camera/_index.md b/docs/operate/reference/components/camera/_index.md
deleted file mode 100644
index f26f024004..0000000000
--- a/docs/operate/reference/components/camera/_index.md
+++ /dev/null
@@ -1,116 +0,0 @@
----
-title: "Camera Component"
-linkTitle: "Camera"
-childTitleEndOverwrite: "Camera Component"
-weight: 40
-type: "docs"
-description: "A camera captures 2D or 3D images and sends them to the computer controlling the machine."
-no_list: true
-tags: ["camera", "components"]
-icon: true
-images: ["/icons/components/camera.svg"]
-modulescript: true
-aliases:
- - "/tutorials/configure-a-camera"
- - "/components/camera/"
-date: "2024-10-21"
-# SMEs: Bijan, vision team
----
-
-The camera component provides an API for getting images or point clouds.
-
-If you have a physical camera or software that generates 2D images or 3D point clouds, use a camera component.
-
-You can use the camera component to configure a webcam, lidar, time-of-flight sensor, or another type of camera.
-You can also use camera models to manipulate the output of other cameras to transform, crop, or otherwise change the output.
-
-{{}}
-
-## Configuration
-
-To use a camera with your machine, you need to add it to your machine's configuration.
-Go to your machine's **CONFIGURE** page, and add a model that supports your camera.
-
-The following list shows the available camera models.
-The configuration of your camera component depends on your camera model.
-For additional configuration information, click on the model name:
-
-{{< tabs >}}
-{{% tab name="viam-server" %}}
-
-{{}}
-
-{{< readfile "/static/include/create-your-own-mr.md" >}}
-
-{{% /tab %}}
-{{% tab name="Micro-RDK" %}}
-
-
-| Model | Description |
-| ----- | ----------- |
-| [`fake`](fake-micro-server/) | A camera model for testing. |
-| [`esp32-camera`](esp32-camera/) | A camera on an ESP32. |
-
-{{% readfile "/static/include/micro-create-your-own.md" %}}
-
-{{% /tab %}}
-{{< /tabs >}}
-
-## API
-
-The [camera API](/reference/apis/components/camera/) supports the following methods:
-
-{{< readfile "/static/include/components/apis/generated/camera-table.md" >}}
-
-## Troubleshooting
-
-If your camera is not working as expected, follow these steps:
-
-1. Check your machine logs on the **LOGS** tab to check for errors.
-1. Review your camera model's documentation to ensure you have configured all required attributes.
-1. Click on the **TEST** panel on the **CONFIGURE** or **CONTROL** tab and test if you can use the camera there.
-1. Disconnect and reconnect your camera.
-
-If none of these steps work, reach out to us on the [Community Discord](https://discord.gg/viam) and we will be happy to help.
-
-### Common errors
-
-{{% expand "Failed to find the best driver that fits the constraints" %}}
-
-When working with a [camera](/operate/reference/components/camera/) component, depending on the camera, you may need to explicitly provide some camera-specific configuration parameters.
-
-Check the specifications for your camera, and manually provide configuration parameters such as width and height to the camera component configuration panel.
-On the **CONFIGURE** page, find your camera, then fill in your camera's specific configuration either using the **Show more** button to show the relevant configuration options, or the **JSON** button to enter these attributes manually.
-Provide at least the width and height values to start.
-
-{{% /expand%}}
-
-{{% expand "Resource locking" %}}
-
-If you are using the Camera API in conjunction with tools like OpenCV, you can not grab images using the third party tool from the camera device while in use with the Camera API.
-
-If you are running into this issue, consider the following solutions:
-
-- using the Camera API to get images and then passing them to your third party tool.
-- run a `viam-server` on a more powerful machine and configure it as a {{< glossary_tooltip term_id="remote-part" text="remote part" >}} with a vision service to evaluate images.
-
-{{% /expand%}}
-
-## Next steps
-
-For general configuration, development, and usage info, see:
-
-{{< cards >}}
-{{% card link="/data/capture-sync/capture-and-sync-data/" noimage="true" %}}
-{{% card link="/operate/control/web-app/" noimage="true" %}}
-{{< /cards >}}
-
-You can also use the camera component with the following services:
-
-- [Data management service](/data/capture-sync/capture-and-sync-data/): To capture and sync the camera's data
-- [Vision service](/reference/services/vision/): To use computer vision to interpret the camera stream
-- [SLAM service](/operate/reference/services/slam/): For mapping (with a depth camera)
-
-{{% hiddencontent %}}
-There's no model of transform camera available to mirror the camera image.
-{{% /hiddencontent %}}
diff --git a/docs/operate/reference/components/camera/calibrate.md b/docs/operate/reference/components/camera/calibrate.md
deleted file mode 100644
index 4ae3eb7c6b..0000000000
--- a/docs/operate/reference/components/camera/calibrate.md
+++ /dev/null
@@ -1,118 +0,0 @@
----
-title: "Calibrate a Camera"
-linkTitle: "Calibrate a Camera"
-weight: 80
-type: "docs"
-description: "Calibrate a camera and extract the intrinsic and distortion parameters."
-images: ["/icons/components/camera.svg"]
-tags: ["camera", "components"]
-aliases:
- - "/components/camera/calibrate/"
-no_component: true
-date: "2024-01-01"
-# updated: "" # When the content was last entirely checked
----
-
-To calibrate a camera, you can use the classical example of a [chessboard](https://en.wikipedia.org/wiki/Chessboard_detection).
-The chessboard is often used because the geometry makes it a good test case for detection and processing.
-
-## Prerequisites
-
-The calibration code uses the `numpy` and `opencv-python` packages.
-To follow along, install the libraries:
-
-```sh {class="command-line" data-prompt="$"}
-pip3 install numpy
-pip3 install opencv-python
-```
-
-### Instructions
-
-1. Print out the [checkerboard](https://github.com/viam-labs/camera-calibration/blob/main/Checkerboard-A4-25mm-8x6.pdf) and attach it to a flat surface that doesn't distort the checkerboard.
- Good surfaces are completely flat like a table, an non-textured wall or an acrylic plate.
- Do not hold the image in the air with your hands or tape it to a textured surface such as a textured wall, cardboard, or folder.
-2. Take images of the checkerboard with your camera from various angles and distances that show the entire image, including the edges.
- Ensure the image is well and thoroughly lit to avoid distortions affecting the vision algorithms.
- You can use the **Export screenshot** button on the camera panel of your machine's **CONTROL** tab.
- Save between 10 - 15 images (see [examples](https://github.com/viam-labs/camera-calibration#example-images)).
-
- {{< alert title="Important" color="note" >}}
- In order for the calibration to be compatible with the {{< glossary_tooltip term_id="rdk" text="RDK" >}}, take the images by running the camera using the RDK.
- {{< /alert >}}
-
- Example of good images:
- 
-
-3. Save [`cameraCalib.py`](https://github.com/viam-labs/camera-calibration/blob/main/cameraCalib.py)
-4. Run `python3 cameraCalib.py YOUR_PICTURES_DIRECTORY`.
-
- Example output:
-
- ```json {class="line-numbers linkable-line-numbers"}
- "intrinsic_parameters": {
- "fy": 940.2928257873841,
- "height_px": 480,
- "ppx": 320.6075282958033,
- "ppy": 239.14408757087756,
- "width_px": 640,
- "fx": 939.2693584627577
- },
- "distortion_parameters": {
- "rk2": 0.8002516496932317,
- "rk3": -5.408034254951954,
- "tp1": -0.000008996658362365533,
- "tp2": -0.002828504714921335,
- "rk1": 0.046535971648456166
- }
- ```
-
-5. Copy the output which contains the `intrinsic_parameters` and `distortion_parameters` into the JSON config on your machine's **CONFIGURE** tab.
-
-{{}}
-
-The following is a full example config:
-
-```json {class="line-numbers linkable-line-numbers"}
-{
- "components": [
- {
- "name": "color",
- "model": "webcam",
- "api": "rdk:component:camera",
- "attributes": {
- "intrinsic_parameters": {
- "fy": 940.2928257873841,
- "height_px": 480,
- "ppx": 320.6075282958033,
- "ppy": 239.14408757087756,
- "width_px": 640,
- "fx": 939.2693584627577
- },
- "distortion_parameters": {
- "rk2": 0.8002516496932317,
- "rk3": -5.408034254951954,
- "tp1": -0.000008996658362365533,
- "tp2": -0.002828504714921335,
- "rk1": 0.046535971648456166
- },
- "stream": "",
- "debug": false,
- "format": "",
- "video_path": "video0",
- "width_px": 0,
- "height_px": 0
- },
- "depends_on": []
- }
- ]
-}
-```
-
-## Next steps
-
-For more configuration and usage info, see:
-
-{{< cards >}}
-{{% card link="/reference/apis/components/camera/" customTitle="Camera API" noimage="true" %}}
-{{% card link="/data/capture-sync/capture-and-sync-data/" noimage="true" %}}
-{{< /cards >}}
diff --git a/docs/operate/reference/components/camera/esp32-camera.md b/docs/operate/reference/components/camera/esp32-camera.md
deleted file mode 100644
index 5f90943043..0000000000
--- a/docs/operate/reference/components/camera/esp32-camera.md
+++ /dev/null
@@ -1,205 +0,0 @@
----
-title: "Configure an esp32-camera (Micro-RDK)"
-linkTitle: "esp32-camera (Micro-RDK)"
-weight: 33
-type: "docs"
-description: "Configure a camera connected to an esp32 board, initialized and configured using esp-idf."
-images: ["/icons/components/camera.svg"]
-tags: ["camera", "components", "Micro-RDK"]
-component_description: "An `OV2640` or `OV3660` camera connected to an esp32 board."
-micrordk_component: true
-toc_hide: true
-date: "2024-08-28"
-aliases:
- - /components/camera/esp32-camera/
-# updated: "" # When the content was last entirely checked
-# SMEs: Matt Perez, Micro-RDK team
----
-
-`esp32-camera` is the camera model that supports all cameras that work with Espressif's [esp32-camera drivers](https://github.com/espressif/esp32-camera) including:
-
-- `OV2640`
-
- - [Datasheet](https://www.uctronics.com/download/OV2640_DS.pdf)
- - 1600 x 1200, Color, ¼” lens
- - You can use a cam ribbon adapter to connect to your `esp32` board
-
-- `OV3660`: an [m5 camera timer module](https://docs.m5stack.com/en/unit/timercam_f)
- - [Datasheet](https://m5stack.oss-cn-shenzhen.aliyuncs.com/resource/docs/datasheet/unit/OV3660_CSP3_DS_1.3_sida.pdf)
- - 2048 x 1536, Color, ⅕” lens
-
-For example, the `OV2640` with a ribbon cable connected to an ESP32:
-
-{{< imgproc src="/components/camera/esp32-camera-2640.png" alt="Fake Camera on the ESP32" resize="300x" class="shadow" >}}
-
-{{< alert title="Software requirements" color="note" >}}
-To use this model, you must follow the [Set up an ESP32 guide](/operate/install/setup-micro/#build-and-flash-custom-firmware), which enables you to install and activate the ESP-IDF.
-When you create a new project with `cargo generate`, select the option to include camera module traits when prompted.
-Finish building and flashing custom firmware, then return to this guide.
-{{< /alert >}}
-
-{{< alert title="Data management not supported" color="caution" >}}
-
-The `esp32-camera` camera model does not currently support the [data management service](/data/capture-sync/capture-and-sync-data/).
-
-{{< /alert >}}
-
-First, connect your camera to your machine's microcontroller and turn the microcontroller on.
-Then, configure your camera:
-
-{{< tabs name="Configure a esp32-camera" >}}
-{{% tab name="JSON Template" %}}
-
-Navigate to the **CONFIGURE** tab of your machine's page.
-Select **JSON** mode.
-Copy and paste the following JSON into your existing machine configuration into the `"components"` array:
-
-```json {class="line-numbers linkable-line-numbers"}
-{
- "name": "my-esp32camera",
- "api": "rdk:component:camera",
- "model": "esp32-camera",
- "attributes": {
- "pin_d4": ,
- "jpeg_quality": ,
- "frame_size": ,
- "pin_d5": ,
- "pin_d3": ,
- "pin_d6": ,
- "pin_vsync": ,
- "ledc_timer": ,
- "pin_d7": ,
- "pin_sccb_sda": ,
- "pin_href": ,
- "pin_sccb_scl": ,
- "sccb_i2c_port": ,
- "pin_d1": ,
- "pin_d0": ,
- "pin_xclk": ,
- "pin_reset": ,
- "pin_pclk": ,
- "pin_d2": ,
- "xclk_freq_hz": ,
- "ledc_channel":
- }
-}
-```
-
-Edit and fill in the attributes as applicable.
-
-{{% /tab %}}
-{{% tab name="JSON Example: OV2640" %}}
-
-```json {class="line-numbers linkable-line-numbers"}
-{
- "name": "my-esp32camera",
- "api": "rdk:component:camera",
- "model": "esp32-camera",
- "attributes": {
- "pin_pwdn": -1,
- "pin_reset": -1,
- "pin_xclk": 21,
- "pin_sccb_sda": 26,
- "pin_sccb_scl": 27,
- "pin_d7": 35,
- "pin_d6": 34,
- "pin_d5": 39,
- "pin_d4": 36,
- "pin_d3": 19,
- "pin_d2": 18,
- "pin_d1": 5,
- "pin_d0": 4,
- "pin_vsync": 25,
- "pin_href": 23,
- "pin_pclk": 22,
- "xclk_freq_hz": 20000000,
- "ledc_timer": 1,
- "ledc_channel": 1
- }
-}
-```
-
-{{% /tab %}}
-{{% tab name="JSON Example: OV3660" %}}
-
-```json {class="line-numbers linkable-line-numbers"}
-{
- "name": "my-esp32camera",
- "api": "rdk:component:camera",
- "model": "esp32-camera",
- "attributes": {
- "pin_d4": 39,
- "jpeg_quality": 32,
- "frame_size": 5,
- "pin_d5": 18,
- "pin_d3": 5,
- "pin_d6": 36,
- "pin_vsync": 22,
- "ledc_timer": 1,
- "pin_d7": 19,
- "pin_sccb_sda": 25,
- "pin_href": 26,
- "pin_sccb_scl": 23,
- "sccb_i2c_port": -1,
- "pin_d1": 35,
- "pin_d0": 32,
- "pin_xclk": 27,
- "pin_reset": 15,
- "pin_pclk": 21,
- "pin_d2": 34,
- "xclk_freq_hz": 20000000,
- "ledc_channel": 1
- }
-}
-```
-
-{{% /tab %}}
-{{< /tabs >}}
-
-{{% alert title="Note" color="note" %}}
-While the following attributes marked as **Optional** do have defaults, it is recommended that you configure them according to your datasheet as your device may not align with the defaults which could cause damage to your board or camera.
-{{% /alert %}}
-
-The following attributes are available for `esp32-camera` cameras:
-
-
-| Name | Type | Required? | Description |
-| ---- | ---- | --------- | ----------- |
-| `pin_pwdn` | int | Optional | GPIO pin for camera power down line. Default: -1 |
-| `pin_reset` | int | Optional | GPIO pin for camera reset line. Default: -1 |
-| `pin_xclk` | int | Optional | GPIO pin for camera XCLK line. Default: 21 |
-| `pin_sccb_sda` | int | Optional | GPIO pin for camera SDA line. Default: 26 |
-| `pin_sccb_scl` | int | Optional | GPIO pin for camera SCL line. Default: 27 |
-| `pin_d7` | int | Optional | GPIO pin for camera D7 line. Default: 35|
-| `pin_d6` | int | Optional | GPIO pin for camera D6 line. Default: 34 |
-| `pin_d5` | int | Optional | GPIO pin for camera D5 line. Default: 39 |
-| `pin_d4` | int | Optional | GPIO pin for camera D4 line. Default: 36 |
-| `pin_d3` | int | Optional | GPIO pin for camera D3 line. Default: 19 |
-| `pin_d2` | int | Optional | GPIO pin for camera D2 line. Default: 18 |
-| `pin_d1` | int | Optional | GPIO pin for camera D1 line. Default: 5 |
-| `pin_d0` | int | Optional | GPIO pin for camera D0 line. Default: 4 |
-| `pin_vsync` | int | Optional | GPIO pin for camera VSYNC line. Default: 25 |
-| `pin_href` | int | Optional | GPIO pin for camera HREF line. Default: 23 |
-| `pin_pclk` | int | Optional | GPIO pin for camera PLCK line. Default: 22 |
-| `xclk_freq_hz` | int | Optional | Frequency of XCLK signal, in Hz. Experimental: Set to 16MHz on ESP32-S2 or ESP32-S3 to enable EDMA mode. Default: 20000000 |
-| `ledc_timer` | int | Optional | LEDC timer to generate XCLK. Default: 1 |
-| `ledc_channel` | int | Optional | LEDC channel to generate XCLK. Default: 1 |
-| `frame_size` | int | Optional | Size of the output image. Default: 1 |
-| `jpeg_quality` | int | Optional | Quality of JPEG output. Lower means higher quality. Range: 0-63 Default: 32 |
-
-## View the camera stream
-
-{{< readfile "/static/include/components/camera-view-camera-stream.md" >}}
-
-## Troubleshooting
-
-{{< readfile "/static/include/components/troubleshoot/camera.md" >}}
-
-## Next steps
-
-For more configuration and usage info, see:
-
-{{< cards >}}
-{{% card link="/reference/apis/components/camera/" customTitle="Camera API" noimage="true" %}}
-{{% card link="/data/capture-sync/capture-and-sync-data/" noimage="true" %}}
-{{< /cards >}}
diff --git a/docs/operate/reference/components/camera/fake-micro-server.md b/docs/operate/reference/components/camera/fake-micro-server.md
deleted file mode 100644
index a59768e31d..0000000000
--- a/docs/operate/reference/components/camera/fake-micro-server.md
+++ /dev/null
@@ -1,70 +0,0 @@
----
-title: "Configure a Fake Camera (Micro-RDK)"
-linkTitle: "fake (Micro-RDK)"
-weight: 20
-type: "docs"
-description: Configure a camera to use for testing."
-images: ["/icons/components/camera.svg"]
-tags: ["camera", "components"]
-aliases:
- - /components/camera/fake-micro-server/
-component_description: "A camera model for testing."
-micrordk_component: true
-toc_hide: true
-# SMEs: Matt Perez, Micro-RDK team
----
-
-A `fake` camera is a camera model for testing.
-The camera always returns the same image, which is an image of a circle inside a diamond.
-
-{{< alert title="Software requirements" color="note" >}}
-To use this model, you must follow the [Set up an ESP32 guide](/operate/install/setup-micro/#build-and-flash-custom-firmware), which enables you to install and activate the ESP-IDF.
-When you create a new project with `cargo generate`, select the option to include camera module traits when prompted.
-Finish building and flashing custom firmware, then return to this guide.
-{{< /alert >}}
-
-{{< tabs name="Configure a Fake Camera" >}}
-{{% tab name="Config Builder" %}}
-
-Navigate to the **CONFIGURE** tab of your machine's page.
-Click the **+** icon next to your machine part in the left-hand menu and select **Component**.
-Select the `camera` type, then select the `fake` model.
-Enter a name or use the suggested name for your camera and click **Create**.
-
-
-
-{{% /tab %}}
-{{% tab name="JSON Template" %}}
-
-```json {class="line-numbers linkable-line-numbers"}
-{
- "name": "",
- "model": "fake",
- "api": "rdk:component:camera",
- "attributes": {}
-}
-```
-
-{{% /tab %}}
-{{< /tabs >}}
-
-## View the camera stream
-
-Once your camera is configured, expand the **TEST** section on the configuration pane.
-You will see the live video feed from your camera.
-You can change the refresh frequency as needed to change bandwidth.
-
-{{< imgproc src="/components/camera/fake-micro-server-view.png" alt="Fake Camera Micro Server View" resize="400x" class="shadow" >}}
-
-## Troubleshooting
-
-{{< readfile "/static/include/components/troubleshoot/camera.md" >}}
-
-## Next steps
-
-For more configuration and usage info, see:
-
-{{< cards >}}
-{{% card link="/reference/apis/components/camera/" customTitle="Camera API" noimage="true" %}}
-{{% card link="/data/capture-sync/capture-and-sync-data/" noimage="true" %}}
-{{< /cards >}}
diff --git a/docs/operate/reference/components/camera/fake.md b/docs/operate/reference/components/camera/fake.md
deleted file mode 100644
index fffd1adad4..0000000000
--- a/docs/operate/reference/components/camera/fake.md
+++ /dev/null
@@ -1,83 +0,0 @@
----
-title: "Configure a Fake Camera"
-linkTitle: "fake"
-weight: 10
-type: "docs"
-description: Configure a camera to use for testing."
-images: ["/icons/components/camera.svg"]
-tags: ["camera", "components"]
-aliases:
- - "/components/camera/fake/"
-component_description: "A camera model for testing."
-toc_hide: true
-# SMEs: Bijan, vision team
----
-
-A `fake` camera is a camera model for testing.
-The camera always returns the same image, which is an image of a gradient.
-This camera also returns a point cloud.
-
-You can optionally specify a height and width.
-
-{{< tabs name="Configure a Fake Camera" >}}
-{{% tab name="Config Builder" %}}
-
-Navigate to the **CONFIGURE** tab of your machine's page.
-Click the **+** icon next to your machine part in the left-hand menu and select **Component or service**.
-Select the `camera` type, then select the `fake` model.
-Enter a name or use the suggested name for your camera and click **Create**.
-
-
-
-Edit the attributes as applicable to your camera, according to the table below.
-
-{{% /tab %}}
-{{% tab name="JSON Template" %}}
-
-```json {class="line-numbers linkable-line-numbers"}
-{
- "name": "",
- "model": "fake",
- "api": "rdk:component:camera",
- "attributes": {
- "width": ,
- "height":
- }
-}
-```
-
-{{% /tab %}}
-{{< /tabs >}}
-
-The following attributes are available for `fake` cameras:
-
-
-| Name | Type | Required? | Description |
-| ---- | ---- | --------- | ----------- |
-| `width` | int | Optional | The width of the image in pixels. The maximum width is 10000. Default: `1280` |
-| `height` | int | Optional | The height of the image in pixels. The maximum height is 10000. Default: `720` |
-| `animated` | bool | Optional | If you want the camera stream visible on the **CONTROL** tab to be animated. Default: `False` |
-| `rtp_passthrough` | bool | Optional | If true, `GetImages` will ignore width, height, and animated config params. Default: `False`. |
-| `model` | bool | Optional | If true, provides `IntrinsicParams` and `DistortionParams` for the camera. Default: `False`. |
-
-## View the camera stream
-
-Once your camera is configured, go to the **CONTROL** tab, and click on the camera's dropdown menu.
-Then toggle the camera or the Point Cloud Data view to ON.
-You will see the live video feed from your camera.
-You can change the refresh frequency as needed to change bandwidth.
-
-{{< imgproc src="/components/camera/fake-view.png" alt="Fake Camera View" resize="600x" class="shadow" >}}
-
-## Troubleshooting
-
-{{< readfile "/static/include/components/troubleshoot/camera.md" >}}
-
-## Next steps
-
-For more configuration and usage info, see:
-
-{{< cards >}}
-{{% card link="/reference/apis/components/camera/" customTitle="Camera API" noimage="true" %}}
-{{% card link="/data/capture-sync/capture-and-sync-data/" noimage="true" %}}
-{{< /cards >}}
diff --git a/docs/operate/reference/components/camera/ffmpeg.md b/docs/operate/reference/components/camera/ffmpeg.md
deleted file mode 100644
index ab5f4dd893..0000000000
--- a/docs/operate/reference/components/camera/ffmpeg.md
+++ /dev/null
@@ -1,126 +0,0 @@
----
-title: "Configure an ffmpeg Camera"
-linkTitle: "ffmpeg"
-weight: 30
-type: "docs"
-description: "Uses a camera device, a video file, or a stream as a camera."
-images: ["/icons/components/camera.svg"]
-tags: ["camera", "components"]
-aliases:
- - "/components/camera/ffmpeg/"
-component_description: "Use a camera, a video file, or a stream as a camera component."
-toc_hide: true
-# SMEs: Sean Yu, audio/video team
----
-
-The `ffmpeg` camera model uses a camera device, a video file, or a stream as a camera.
-
-When used with a streaming camera, the `ffmpeg` camera model supports any streaming camera format that is supported by the [`ffmpeg` program](https://ffmpeg.org/), including MJPEG, H264, and MP4.
-
-{{< alert title="Note" color="note" >}}
-The [`ffmpeg` program](https://ffmpeg.org/) program must be installed separately from `viam-server` on your system for this driver to work.
-{{< /alert >}}
-
-First, connect your camera to your machine's computer and power both on.
-Then, configure your camera:
-
-{{< tabs name="Configure a ffmpeg camera" >}}
-{{% tab name="Config Builder" %}}
-
-Navigate to the **CONFIGURE** tab of your machine's page.
-Click the **+** icon next to your machine part in the left-hand menu and select **Component or service**.
-Select the `camera` type, then select the `ffmpeg` model.
-Enter a name or use the suggested name for your camera and click **Create**.
-
-
-
-Edit the attributes as applicable to your camera, according to the table below.
-
-{{% /tab %}}
-{{% tab name="JSON Template" %}}
-
-```json {class="line-numbers linkable-line-numbers"}
-{
- "name": "",
- "model": "rdk:builtin:ffmpeg",
- "api": "rdk:component:camera",
- "attributes": {
- "intrinsic_parameters": {
- "width_px": ,
- "height_px": ,
- "fx": ,
- "fy": ,
- "ppx": ,
- "ppy":
- },
- "distortion_parameters": {
- "rk1": ,
- "rk2": ,
- "rk3": ,
- "tp1": ,
- "tp2":
- },
- "video_path": "",
- "input_kw_args": { ... },
- "filters": [
- {
- "name": ,
- "args": [ "", "", ... ],
- "kw_args": { ... }
- }
- ],
- "output_kw_args": { ... },
- }
-}
-```
-
-{{% /tab %}}
-{{% tab name="JSON Example" %}}
-
-```json {class="line-numbers linkable-line-numbers"}
-{
- "name": "stream-camera",
- "api": "rdk:component:camera",
- "model": "rdk:builtin:ffmpeg",
- "attributes": {
- "video_path": "udp://127.0.0.1:23000",
- "distortion_parameters": {},
- "intrinsic_parameters": {
- "height_px": 2486,
- "width_px": 1970
- }
- }
-}
-```
-
-{{% /tab %}}
-{{< /tabs >}}
-
-The following attributes are available for `ffmpeg` cameras:
-
-
-| Name | Type | Required? | Description |
-| ---- | ---- | --------- | ----------- |
-| `video_path` | string | **Required** | The file path to the camera device, color image file, or streaming camera. If you are using a camera with an RTSP stream, provide the RTSP address to this attribute. |
-| `intrinsic_parameters` | object | Optional | The intrinsic parameters of the camera used to do 2D <-> 3D projections:
width_px: The expected width of the aligned image in pixels. Value must be positive.
height_px: The expected height of the aligned image in pixels. Value must be positive.
fx: The image center x point.
fy: The image center y point.
ppx: The image focal x.
ppy: The image focal y.
|
-| `distortion_parameters` | object | Optional | Modified Brown-Conrady parameters used to correct for distortions caused by the shape of the camera lens:
rk1: The radial distortion x.
rk2: The radial distortion y.
rk3: The radial distortion z.
tp1: The tangential distortion x.
tp2: The tangential distortion y.
|
-| `input_kw_args` | object | Optional | The input keyword arguments. |
-| `filters` | array | Optional | Array of filter objects that specify:
name: The name of the filter.
args: The arguments for the filter.
kw_args: Any keyword arguments for the filter.
|
-| `output_kw_args` | object | Optional | The output keyword arguments. |
-
-## View the camera stream
-
-{{< readfile "/static/include/components/camera-view-camera-stream.md" >}}
-
-## Troubleshooting
-
-{{< readfile "/static/include/components/troubleshoot/camera.md" >}}
-
-## Next steps
-
-For more configuration and usage info, see:
-
-{{< cards >}}
-{{% card link="/reference/apis/components/camera/" customTitle="Camera API" noimage="true" %}}
-{{% card link="/data/capture-sync/capture-and-sync-data/" noimage="true" %}}
-{{< /cards >}}
diff --git a/docs/operate/reference/components/camera/image-file.md b/docs/operate/reference/components/camera/image-file.md
deleted file mode 100644
index 8c152d1015..0000000000
--- a/docs/operate/reference/components/camera/image-file.md
+++ /dev/null
@@ -1,98 +0,0 @@
----
-title: "Configure an Image File Camera"
-linkTitle: "image_file"
-weight: 31
-type: "docs"
-description: "Configure a camera that gets color or depth images frames from a file path."
-images: ["/icons/components/camera.svg"]
-tags: ["camera", "components"]
-aliases:
- - "/components/camera/image-file/"
-component_description: "Gets color and depth image frames from a file path."
-toc_hide: true
-# SMEs: Bijan, vision team
----
-
-An `image_file` camera gets color and depth image frames or point clouds from a file path on your local system.
-
-{{< tabs name="Configure an Image File Camera" >}}
-{{% tab name="Config Builder" %}}
-
-Navigate to the **CONFIGURE** tab of your machine's page.
-Click the **+** icon next to your machine part in the left-hand menu and select **Component or service**.
-Select the `camera` type, then select the `image_file` model.
-Enter a name or use the suggested name for your camera and click **Create**.
-
-
-
-Edit the attributes as applicable to your camera, according to the table below.
-Note that you _must_ specify at least one of `color_image_file_path`, `depth_image_file_path`, and `pointcloud_file_path`.
-
-{{% /tab %}}
-{{% tab name="JSON Template" %}}
-
-```json {class="line-numbers linkable-line-numbers"}
-{
- "name": "",
- "model": "image_file",
- "api": "rdk:component:camera",
- "attributes": {
- "intrinsic_parameters": {
- "width_px": ,
- "height_px": ,
- "fx": ,
- "fy": ,
- "ppx": ,
- "ppy":
- },
- "distortion_parameters": {
- "rk1": ,
- "rk2": ,
- "rk3": ,
- "tp1": ,
- "tp2":
- },
- "color_image_file_path": "",
- "depth_image_file_path": "",
- "pointcloud_file_path": "",
- "preloaded_image: ""
- }
-}
-```
-
-{{% /tab %}}
-{{< /tabs >}}
-
-The following attributes are available for `image_file` cameras:
-
-
-| Name | Type | Required? | Description |
-| ---- | ---- | --------- | ----------- |
-| `intrinsic_parameters` | object | Optional | The intrinsic parameters of the camera used to do 2D <-> 3D projections:
width_px: The expected width of the aligned image in pixels. Value must be >= 0.
height_px: The expected height of the aligned image in pixels. Value must be >= 0.
fx: The image center x point.
fy: The image center y point.
ppx: The image focal x.
ppy: The image focal y.
|
-| `distortion_parameters` | object | Optional | Modified Brown-Conrady parameters used to correct for distortions caused by the shape of the camera lens:
rk1: The radial distortion x.
rk2: The radial distortion y.
rk3: The radial distortion z.
tp1: The tangential distortion x.
tp2: The tangential distortion y.
|
-| `color_image_file_path` | string | Optional | The file path to the color image on your local system. |
-| `depth_image_file_path` | string | Optional | The file path to the depth image on your local system. |
-| `pointcloud_file_path` | string | Optional | The file path to the point cloud file on your local system. |
-| `preloaded_image` | string | Optional | Select a pre-loaded image for the camera to display. Options: `"pizza"`, `"dog"`, `"crowd"`. |
-
-You must specify at least one of `color_image_file_path`, `depth_image_file_path`, and `pointcloud_file_path`.
-
-If you provide configuration for the `depth_image_file_path` and `intrinsic_parameters` of the camera, then your machine will also retrieve point cloud data from the `depth_image_file_path`.
-If you then also configure a `pointcloud_file_path` on your camera, Viam will try to pull the data from the `pointcloud_file_path` first.
-
-## View the camera stream
-
-{{< readfile "/static/include/components/camera-view-camera-stream.md" >}}
-
-## Troubleshooting
-
-{{< readfile "/static/include/components/troubleshoot/camera.md" >}}
-
-## Next steps
-
-For more configuration and usage info, see:
-
-{{< cards >}}
-{{% card link="/reference/apis/components/camera/" customTitle="Camera API" noimage="true" %}}
-{{% card link="/data/capture-sync/capture-and-sync-data/" noimage="true" %}}
-{{< /cards >}}
diff --git a/docs/operate/reference/components/camera/transform.md b/docs/operate/reference/components/camera/transform.md
deleted file mode 100644
index 5935fc9c51..0000000000
--- a/docs/operate/reference/components/camera/transform.md
+++ /dev/null
@@ -1,333 +0,0 @@
----
-title: "Transform Camera"
-linkTitle: "transform"
-weight: 60
-type: "docs"
-description: "Use the transform camera model to apply transformation to a camera stream. For example, crop, resize, or add overlays with info about the camera stream."
-images: ["/icons/components/camera.svg"]
-tags: ["camera", "components"]
-aliases:
- - "/components/camera/transform/"
-component_description: "Use pipelines for applying transformations to an input image source."
-toc_hide: true
-# SMEs: Rand, AV team
----
-
-Use the `transform` model to apply transformations to input source images.
-The transformations are applied in the order they are written in the `pipeline`.
-
-{{< tabs name="Example transform view" >}}
-{{% tab name="Config Builder" %}}
-
-Navigate to the **CONFIGURE** tab of your machine's page.
-Click the **+** icon next to your machine part in the left-hand menu and select **Configuration block**.
-Select the `camera` type, then select the `transform` model.
-Enter a name or use the suggested name for your camera and click **Create**.
-
-Copy and paste the following attribute template into the attributes field.
-Then remove and fill in the attributes as applicable to your camera, according to the table below.
-
-{{< tabs >}}
-{{% tab name="Attributes template" %}}
-
-```json {class="line-numbers linkable-line-numbers"}
-{
- "source" : "",
- "pipeline": [
- { "type": "", "attributes": { ... } },
- ],
- "intrinsic_parameters": {
- "width_px": ,
- "height_px": ,
- "fx": ,
- "fy": ,
- "ppx": ,
- "ppy":
- },
- "distortion_parameters": {
- "rk1": ,
- "rk2": ,
- "rk3": ,
- "tp1": ,
- "tp2":
- }
-}
-```
-
-{{% /tab %}}
-{{% tab name="Attributes example" %}}
-
-```json {class="line-numbers linkable-line-numbers"}
-{
- "source": "my-webcam",
- "pipeline": [
- { "type": "rotate", "attributes": {} },
- { "type": "resize", "attributes": { "width_px": 200, "height_px": 100 } }
- ]
-}
-```
-
-{{% /tab %}}
-{{< /tabs >}}
-
-{{% /tab %}}
-{{% tab name="JSON Template" %}}
-
-```json {class="line-numbers linkable-line-numbers"}
-{
- "name": "",
- "model": "transform",
- "api": "rdk:component:camera",
- "attributes" : {
- "source" : "",
- "pipeline": [
- { "type": "", "attributes": { ... } },
- ],
- "intrinsic_parameters": {
- "width_px": ,
- "height_px": ,
- "fx": ,
- "fy": ,
- "ppx": ,
- "ppy":
- },
- "distortion_parameters": {
- "rk1": ,
- "rk2": ,
- "rk3": ,
- "tp1": ,
- "tp2":
- }
- }
-}
-```
-
-{{% /tab %}}
-{{< /tabs >}}
-
-The following attributes are available for `transform` views:
-
-
-| Name | Type | Required? | Description |
-| ---- | ---- | --------- | ----------- |
-| `source` | string | **Required** | `name` of the camera to transform. |
-| `pipeline` | array | **Required** | Specify an array of transformation objects. |
-| `intrinsic_parameters` | object | Optional | The intrinsic parameters of the camera used to do 2D <-> 3D projections:
width_px: The expected width of the aligned image in pixels. Value must be >= 0.
height_px: The expected height of the aligned image in pixels. Value must be >= 0.
fx: The image center x point.
fy: The image center y point.
ppx: The image focal x.
ppy: The image focal y.
|
-| `distortion_parameters` | object | Optional | Modified Brown-Conrady parameters used to correct for distortions caused by the shape of the camera lens:
rk1: The radial distortion x.
rk2: The radial distortion y.
rk3: The radial distortion z.
tp1: The tangential distortion x.
tp2: The tangential distortion y.
|
-
-The following are the transformation objects available for the `pipeline`:
-
-## Classifications
-
-Classifications overlay text from the `GetClassifications` method of the [vision service](/reference/services/vision/) onto the image.
-
-```json {class="line-numbers linkable-line-numbers"}
-{
- "source": "",
- "pipeline": [
- {
- "type": "classifications",
- "attributes": {
- "classifier_name": "",
- "confidence_threshold": ,
- "max_classifications": ,
- "valid_labels": [ "