# Add a switch

Add a switch to your machine’s configuration so you can read and set switch positions from the Viam app and from code.

## Concepts

A switch component represents a multi-position switch. The API provides:

- **GetPosition**: read the current switch position (as a number).
- **SetPosition**: set the switch to a specific position.
- **GetNumberOfPositions**: query how many positions the switch has, along with optional labels for each position.

Physical switch hardware typically comes from a **module in the registry** that reads GPIO pins or communicates with a switch controller.

The `fake` built-in model simulates a switch with configurable positions and is useful for testing. Search for `switch` in the [Viam registry](https://app.viam.com/registry) to see available models.

### Built-in models

- [`fake`](https://docs.viam.com/reference/components/switch/fake/) — A model used for testing, with no physical hardware.

### Registry modules

For hardware the built-in models don’t cover, search for `switch` in the [Viam registry](https://app.viam.com/registry). Each module’s configuration is documented on its registry page.

## Steps

### 1. Add a switch component

1. Click the **+** button.
2. Select **Blocks**.
3. Search for the model that matches your switch hardware. Search by manufacturer name, chip, or device type.
4. Name your switch (for example, `my-switch`) and click **Add to machine**.

### 2. Configure switch attributes

**For the `fake` model:**

```json
{
  "position_count": 3,
  "labels": ["off", "low", "high"]
}
```

| Attribute          | Type                | Required | Description                                                                            |
|--------------------|---------------------|----------|----------------------------------------------------------------------------------------|
| `position_count`   | int                 | No       | Number of positions. Defaults to 2.                                                  |
| `labels`           | list of strings     | No       | Human-readable labels for each position. Must match `position_count` if provided.    |

For a physical switch with a registry module, check the module’s documentation for required attributes.

### 3. Save and test

Click **Save**, then expand the **Test** section.

- Read the current position.
- Set the switch to different positions and verify it responds correctly.

## Try it

Read the switch position, cycle through positions, and read labels.

To get the credentials for the code below, go to your machine’s page in the Viam app, click the **CONNECT** tab, and select **API keys**.
Copy the **Key** and **ID**. 
Then click the **CONFIGURE** tab, and click **Details**, and copy the **Remote address**. 
When you run the code below, you’ll see the switch cycle through all positions. With the fake model, positions update in memory. With real hardware, verify the switch physically changes state.

- [Python](https://docs.viam.com/hardware/common-components/add-a-switch/#tabset-hardwarecommon-componentsadd-a-switch-1-0)
- [Go](https://docs.viam.com/hardware/common-components/add-a-switch/#tabset-hardwarecommon-componentsadd-a-switch-1-1)

Install the Viam Python SDK in a virtual environment by following [Install the Python SDK](https://docs.viam.com/reference/sdks/python/python-venv/).

Save this as `switch_test.py`:

```python
import asyncio
from viam.robot.client import RobotClient
from viam.components.switch import Switch

async def main():
    opts = RobotClient.Options.with_api_key(
        api_key="YOUR-API-KEY",
        api_key_id="YOUR-API-KEY-ID"
    )
    robot = await RobotClient.at_address("YOUR-MACHINE-ADDRESS", opts)

switch = Switch.from_robot(robot, "my-switch")

# Get number of positions and labels
    num_positions, labels = await switch.get_number_of_positions()
    print(f"Positions: {num_positions}, Labels: {labels}")

# Get current position
    position = await switch.get_position()
    print(f"Current position: {position}")

# Cycle through all positions
    for pos in range(num_positions):
        await switch.set_position(pos)
        current = await switch.get_position()
        label = labels[pos] if labels else str(pos)
        print(f"Set to position {pos} ({label}), read back: {current}")

await robot.close()

if __name__ == "__main__":
    asyncio.run(main())
```

Run it:

```bash
python switch_test.py
```

```bash
mkdir switch-test && cd switch-test
go mod init switch-test
go get go.viam.com/rdk
```

Save this as `main.go`:

```go
package main

import (
    "context"
    "fmt"

toggleswitch "go.viam.com/rdk/components/switch"
    "go.viam.com/rdk/logging"
    "go.viam.com/rdk/robot/client"
)

func main() {
    ctx := context.Background()
    logger := logging.NewLogger("switch-test")

robot, err := client.New(ctx, "YOUR-MACHINE-ADDRESS", logger,
        client.WithDialOptions(client.WithEntityCredentials(
            "YOUR-API-KEY-ID",
            client.Credentials{
                Type:    client.CredentialsTypeAPIKey,
                Payload: "YOUR-API-KEY",
            })),
    )
    if err != nil {
        logger.Fatal(err)
    }
    defer robot.Close(ctx)

sw, err := toggleswitch.FromProvider(robot, "my-switch")
    if err != nil {
        logger.Fatal(err)
    }

// Get number of positions and labels
    numPositions, labels, err := sw.GetNumberOfPositions(ctx, nil)
    if err != nil {
        logger.Fatal(err)
    }
    fmt.Printf("Positions: %d, Labels: %v\n", numPositions, labels)

// Get current position
    position, err := sw.GetPosition(ctx, nil)
    if err != nil {
        logger.Fatal(err)
    }
    fmt.Printf("Current position: %d\n", position)

// Cycle through all positions
    for pos := uint32(0); pos < numPositions; pos++ {
        if err := sw.SetPosition(ctx, pos, nil); err != nil {
            logger.Fatal(err)
        }
        current, err := sw.GetPosition(ctx, nil)
        if err != nil {
            logger.Fatal(err)
        }
        label := fmt.Sprintf("%d", pos)
        if int(pos) < len(labels) {
            label = labels[pos]
        }
        fmt.Printf("Set to position %d (%s), read back: %d\n", pos, label, current)
    }
}
```

Run it:

```bash
go run main.go
```

## Troubleshooting

##### SetPosition returns an error

- Verify the position value is within the valid range (0 to `GetNumberOfPositions - 1`).
- Check the module’s documentation for any constraints on position changes.

##### GetPosition returns unexpected values

- Some physical switches may bounce between positions. Check if your module supports debouncing configuration.
- Verify the wiring matches the expected position mapping.
