Skip to content

Devices

Device templates and registry for common components.

devices

Device templates and registry for pinviz.

DeviceRegistry

DeviceRegistry()

Central registry for device templates.

Source code in src/pinviz/devices/registry.py
def __init__(self):
    self._templates: dict[str, DeviceTemplate] = {}

create

create(type_id: str, **kwargs: Any) -> Device

Create a device instance from a template.

PARAMETER DESCRIPTION
type_id

Device type identifier

TYPE: str

**kwargs

Parameters to pass to the factory function

TYPE: Any DEFAULT: {}

RETURNS DESCRIPTION
Device

Device instance

RAISES DESCRIPTION
ValueError

If device type is not registered

Source code in src/pinviz/devices/registry.py
def create(self, type_id: str, **kwargs: Any) -> Device:
    """
    Create a device instance from a template.

    Args:
        type_id: Device type identifier
        **kwargs: Parameters to pass to the factory function

    Returns:
        Device instance

    Raises:
        ValueError: If device type is not registered
    """
    template = self.get(type_id)
    if not template:
        raise ValueError(f"Unknown device type: {type_id}")

    return template.factory(**kwargs)

get

get(type_id: str) -> DeviceTemplate | None

Get a device template by type ID.

Source code in src/pinviz/devices/registry.py
def get(self, type_id: str) -> DeviceTemplate | None:
    """Get a device template by type ID."""
    return self._templates.get(type_id.lower())

get_categories

get_categories() -> list[str]

Get all unique device categories.

Source code in src/pinviz/devices/registry.py
def get_categories(self) -> list[str]:
    """Get all unique device categories."""
    categories = {t.category for t in self._templates.values()}
    return sorted(categories)

list_all

list_all() -> list[DeviceTemplate]

Get all registered device templates.

Source code in src/pinviz/devices/registry.py
def list_all(self) -> list[DeviceTemplate]:
    """Get all registered device templates."""
    return list(self._templates.values())

list_by_category

list_by_category(category: str) -> list[DeviceTemplate]

Get all device templates in a specific category.

Source code in src/pinviz/devices/registry.py
def list_by_category(self, category: str) -> list[DeviceTemplate]:
    """Get all device templates in a specific category."""
    return [t for t in self._templates.values() if t.category == category]

register

register(
    type_id: str,
    name: str,
    description: str,
    category: str,
    factory: Callable[..., Device],
    parameters: dict[str, Any] | None = None,
    url: str | None = None,
) -> None

Register a device template.

PARAMETER DESCRIPTION
type_id

Unique identifier for the device type (used in YAML configs)

TYPE: str

name

Human-readable device name

TYPE: str

description

Brief description of the device

TYPE: str

category

Device category (sensors, displays, leds, io, etc.)

TYPE: str

factory

Factory function that creates the device

TYPE: Callable[..., Device]

parameters

Optional dict describing factory parameters

TYPE: dict[str, Any] | None DEFAULT: None

url

Optional URL to device documentation or datasheet

TYPE: str | None DEFAULT: None

Source code in src/pinviz/devices/registry.py
def register(
    self,
    type_id: str,
    name: str,
    description: str,
    category: str,
    factory: Callable[..., Device],
    parameters: dict[str, Any] | None = None,
    url: str | None = None,
) -> None:
    """
    Register a device template.

    Args:
        type_id: Unique identifier for the device type (used in YAML configs)
        name: Human-readable device name
        description: Brief description of the device
        category: Device category (sensors, displays, leds, io, etc.)
        factory: Factory function that creates the device
        parameters: Optional dict describing factory parameters
        url: Optional URL to device documentation or datasheet
    """
    template = DeviceTemplate(
        type_id=type_id,
        name=name,
        description=description,
        category=category,
        factory=factory,
        parameters=parameters,
        url=url,
    )
    self._templates[type_id.lower()] = template

DeviceTemplate dataclass

DeviceTemplate(
    type_id: str,
    name: str,
    description: str,
    category: str,
    factory: Callable[..., Device],
    parameters: dict[str, Any] | None = None,
    url: str | None = None,
)

Metadata for a device template.

bh1750_light_sensor

bh1750_light_sensor() -> Device

BH1750 I2C ambient light sensor.

Pinout (top to bottom): - VCC: 3.3V power - GND: Ground - SCL: I2C clock - SDA: I2C data - ADDR: I2C address select (optional)

Source code in src/pinviz/devices/sensors.py
def bh1750_light_sensor() -> Device:
    """
    BH1750 I2C ambient light sensor.

    Pinout (top to bottom):
    - VCC: 3.3V power
    - GND: Ground
    - SCL: I2C clock
    - SDA: I2C data
    - ADDR: I2C address select (optional)
    """
    pin_spacing = 8.0
    pin_x = 5.0  # Pins on left side of device

    pins = [
        DevicePin("VCC", PinRole.POWER_3V3, Point(pin_x, 10)),
        DevicePin("GND", PinRole.GROUND, Point(pin_x, 10 + pin_spacing)),
        DevicePin("SCL", PinRole.I2C_SCL, Point(pin_x, 10 + pin_spacing * 2)),
        DevicePin("SDA", PinRole.I2C_SDA, Point(pin_x, 10 + pin_spacing * 3)),
        DevicePin("ADDR", PinRole.GPIO, Point(pin_x, 10 + pin_spacing * 4)),
    ]

    return Device(
        name="BH1750",
        pins=pins,
        width=70.0,
        height=60.0,
        color="#4A90E2",
    )

button_switch

button_switch(pull_up: bool = True) -> Device

Push button or switch.

PARAMETER DESCRIPTION
pull_up

True for pull-up configuration, False for pull-down

TYPE: bool DEFAULT: True

Pinout: - SIG: Signal to GPIO - GND or VCC: Ground (pull-up) or VCC (pull-down)

Source code in src/pinviz/devices/io.py
def button_switch(pull_up: bool = True) -> Device:
    """
    Push button or switch.

    Args:
        pull_up: True for pull-up configuration, False for pull-down

    Pinout:
    - SIG: Signal to GPIO
    - GND or VCC: Ground (pull-up) or VCC (pull-down)
    """
    pin_spacing = 12.0
    pin_x = 5.0

    if pull_up:
        pins = [
            DevicePin("SIG", PinRole.GPIO, Point(pin_x, 15)),
            DevicePin("GND", PinRole.GROUND, Point(pin_x, 15 + pin_spacing)),
        ]
    else:
        pins = [
            DevicePin("SIG", PinRole.GPIO, Point(pin_x, 15)),
            DevicePin("VCC", PinRole.POWER_3V3, Point(pin_x, 15 + pin_spacing)),
        ]

    config = "Pull-up" if pull_up else "Pull-down"

    return Device(
        name=f"Button ({config})",
        pins=pins,
        width=60.0,
        height=40.0,
        color="#95A5A6",
    )

ds18b20_temp_sensor

ds18b20_temp_sensor() -> Device

DS18B20 waterproof digital temperature sensor (1-Wire protocol).

Pinout (typical wire colors): - VCC: 3.3V power (red wire) - GND: Ground (black wire) - DATA: 1-Wire data line (yellow/white wire)

Note: Requires a 4.7kΩ pull-up resistor between DATA and VCC. Use inline resistor components in connections to add the pull-up resistor.

The DATA pin connects to any available GPIO pin on the Raspberry Pi. Enable 1-Wire in raspi-config and load the w1-gpio kernel module.

Source code in src/pinviz/devices/sensors.py
def ds18b20_temp_sensor() -> Device:
    """
    DS18B20 waterproof digital temperature sensor (1-Wire protocol).

    Pinout (typical wire colors):
    - VCC: 3.3V power (red wire)
    - GND: Ground (black wire)
    - DATA: 1-Wire data line (yellow/white wire)

    Note: Requires a 4.7kΩ pull-up resistor between DATA and VCC.
    Use inline resistor components in connections to add the pull-up resistor.

    The DATA pin connects to any available GPIO pin on the Raspberry Pi.
    Enable 1-Wire in raspi-config and load the w1-gpio kernel module.
    """
    pin_spacing = 10.0
    pin_x = 5.0

    pins = [
        DevicePin("VCC", PinRole.POWER_3V3, Point(pin_x, 12)),
        DevicePin("DATA", PinRole.GPIO, Point(pin_x, 12 + pin_spacing)),
        DevicePin("GND", PinRole.GROUND, Point(pin_x, 12 + pin_spacing * 2)),
    ]

    return Device(
        name="DS18B20",
        pins=pins,
        width=75.0,
        height=45.0,
        color="#F39C12",
    )

generic_i2c_device

generic_i2c_device(
    name: str, has_int_pin: bool = False
) -> Device

Generic I2C device template.

PARAMETER DESCRIPTION
name

Device display name

TYPE: str

has_int_pin

Whether device has an interrupt pin

TYPE: bool DEFAULT: False

Pinout: - VCC: 3.3V power - GND: Ground - SCL: I2C clock - SDA: I2C data - INT: Interrupt (optional)

Source code in src/pinviz/devices/generic.py
def generic_i2c_device(name: str, has_int_pin: bool = False) -> Device:
    """
    Generic I2C device template.

    Args:
        name: Device display name
        has_int_pin: Whether device has an interrupt pin

    Pinout:
    - VCC: 3.3V power
    - GND: Ground
    - SCL: I2C clock
    - SDA: I2C data
    - INT: Interrupt (optional)
    """
    pin_spacing = 8.0
    pin_x = 5.0

    pins = [
        DevicePin("VCC", PinRole.POWER_3V3, Point(pin_x, 10)),
        DevicePin("GND", PinRole.GROUND, Point(pin_x, 10 + pin_spacing)),
        DevicePin("SCL", PinRole.I2C_SCL, Point(pin_x, 10 + pin_spacing * 2)),
        DevicePin("SDA", PinRole.I2C_SDA, Point(pin_x, 10 + pin_spacing * 3)),
    ]

    if has_int_pin:
        pins.append(DevicePin("INT", PinRole.GPIO, Point(pin_x, 10 + pin_spacing * 4)))

    height = 50.0 if has_int_pin else 42.0

    return Device(
        name=name,
        pins=pins,
        width=75.0,
        height=height,
        color="#9B59B6",
    )

generic_spi_device

generic_spi_device(name: str) -> Device

Generic SPI device template.

Pinout: - VCC: 3.3V power - GND: Ground - SCLK: SPI clock - MOSI: Master Out Slave In - MISO: Master In Slave Out - CS: Chip Select

Source code in src/pinviz/devices/generic.py
def generic_spi_device(name: str) -> Device:
    """
    Generic SPI device template.

    Pinout:
    - VCC: 3.3V power
    - GND: Ground
    - SCLK: SPI clock
    - MOSI: Master Out Slave In
    - MISO: Master In Slave Out
    - CS: Chip Select
    """
    pin_spacing = 8.0
    pin_x = 5.0

    pins = [
        DevicePin("VCC", PinRole.POWER_3V3, Point(pin_x, 10)),
        DevicePin("GND", PinRole.GROUND, Point(pin_x, 10 + pin_spacing)),
        DevicePin("SCLK", PinRole.SPI_SCLK, Point(pin_x, 10 + pin_spacing * 2)),
        DevicePin("MOSI", PinRole.SPI_MOSI, Point(pin_x, 10 + pin_spacing * 3)),
        DevicePin("MISO", PinRole.SPI_MISO, Point(pin_x, 10 + pin_spacing * 4)),
        DevicePin("CS", PinRole.SPI_CE0, Point(pin_x, 10 + pin_spacing * 5)),
    ]

    return Device(
        name=name,
        pins=pins,
        width=75.0,
        height=60.0,
        color="#3498DB",
    )

get_registry

get_registry() -> DeviceRegistry

Get the global device registry instance.

Source code in src/pinviz/devices/registry.py
def get_registry() -> DeviceRegistry:
    """Get the global device registry instance."""
    return _registry

ir_led_ring

ir_led_ring(num_leds: int = 12) -> Device

IR LED ring module with control pin.

Pinout: - VCC: 5V power - GND: Ground - CTRL: Control signal (GPIO)

PARAMETER DESCRIPTION
num_leds

Number of LEDs in the ring (for display purposes)

TYPE: int DEFAULT: 12

Source code in src/pinviz/devices/leds.py
def ir_led_ring(num_leds: int = 12) -> Device:
    """
    IR LED ring module with control pin.

    Pinout:
    - VCC: 5V power
    - GND: Ground
    - CTRL: Control signal (GPIO)

    Args:
        num_leds: Number of LEDs in the ring (for display purposes)
    """
    pin_spacing = 10.0
    pin_x = 5.0

    pins = [
        DevicePin("VCC", PinRole.POWER_5V, Point(pin_x, 15)),
        DevicePin("CTRL", PinRole.GPIO, Point(pin_x, 15 + pin_spacing)),
        DevicePin("GND", PinRole.GROUND, Point(pin_x, 15 + pin_spacing * 2)),
    ]

    return Device(
        name=f"IR LED Ring ({num_leds})",
        pins=pins,
        width=80.0,
        height=50.0,
        color="#E24A4A",
    )

simple_led

simple_led(color_name: str = 'Red') -> Device

Simple LED module.

Pinout: - +: Anode (positive terminal, connects to GPIO) - -: Cathode (negative terminal, connects to GND)

Note: Use inline resistor components in connections to add current-limiting resistors. Typical values: 220Ω-330Ω for 3.3V, 470Ω-1kΩ for 5V.

PARAMETER DESCRIPTION
color_name

LED color for display

TYPE: str DEFAULT: 'Red'

Source code in src/pinviz/devices/leds.py
def simple_led(color_name: str = "Red") -> Device:
    """
    Simple LED module.

    Pinout:
    - +: Anode (positive terminal, connects to GPIO)
    - -: Cathode (negative terminal, connects to GND)

    Note: Use inline resistor components in connections to add current-limiting
    resistors. Typical values: 220Ω-330Ω for 3.3V, 470Ω-1kΩ for 5V.

    Args:
        color_name: LED color for display
    """
    pin_spacing = 12.0
    pin_x = 5.0

    pins = [
        DevicePin("+", PinRole.GPIO, Point(pin_x, 15)),
        DevicePin("-", PinRole.GROUND, Point(pin_x, 15 + pin_spacing)),
    ]

    return Device(
        name=f"{color_name} LED",
        pins=pins,
        width=50.0,
        height=40.0,
        color="#E74C3C",
    )