# `BB.Igniter`
[🔗](https://github.com/beam-bots/bb/blob/main/lib/bb/igniter.ex#L6)

Helpers for writing Igniter installers for BB add-on packages.

Only available when `:igniter` is loaded.

# `add_controller`

```elixir
@spec add_controller(Igniter.t(), module(), atom(), String.t()) :: Igniter.t()
```

Adds a `controller` entry to the robot's `controllers do … end` section.

`code` is the full DSL call as a string, e.g.

    controller :dynamixel, {BB.Servo.Robotis.Controller, port: ...}

The section is created if it doesn't already exist. Idempotent on `name`:
if a controller with the same name is already present, the igniter is
returned unchanged.

# `add_param_group`

```elixir
@spec add_param_group(Igniter.t(), module(), [atom(), ...], String.t()) :: Igniter.t()
```

Adds a nested `group` hierarchy to the robot's `parameters do … end` section.

`group_path` is a list of atoms describing the path of nested groups to
create, e.g. `[:config, :feetech]` produces:

    group :config do
      group :feetech do
        <body_code>
      end
    end

`body_code` is the contents of the innermost group (typically one or more
`param ...` declarations) as a string.

The `parameters` section and intermediate groups are created as needed.
Idempotent: if the full group path already exists, the body is not added a
second time (so manually-edited param contents are preserved).

# `add_parameter_bridge`

```elixir
@spec add_parameter_bridge(Igniter.t(), module(), atom(), String.t()) :: Igniter.t()
```

Adds a `bridge` entry to the robot's `parameters do … end` section.

`code` is the full DSL call as a string, e.g.

    bridge :robotis_bridge, {BB.Servo.Robotis.Bridge, controller: :dynamixel}

The section is created if it doesn't already exist. Idempotent on `name`.

# `add_topology_link`

```elixir
@spec add_topology_link(Igniter.t(), module(), atom(), String.t()) :: Igniter.t()
```

Adds a `link` entry to the robot's `topology do … end` section.

`body_code` is the DSL inside the link block as a string, e.g.

    visual do
      origin do z(~u(0.1 meter)) end
    end

    joint :shoulder do
      ...
    end

The section is created if it doesn't already exist. Idempotent on `name`:
if a top-level link with the same name is already present, the igniter is
returned unchanged.

# `populate_link`

```elixir
@spec populate_link(Igniter.t(), module(), [atom(), ...], String.t()) :: Igniter.t()
```

Populates an existing empty `link` in the robot's topology with `body_code`.

`link_path` is the chain of link names from the topology root down to the
leaf to populate, e.g. `[:base_link]` or
`[:base_link, :shoulder_link, :upper_arm_link]`.

Idempotent: if the leaf link already has any DSL entities in its body,
the igniter is returned unchanged so user customisations are preserved.
If the leaf link is empty, `body_code` is inserted as its body.

Returns the igniter unchanged if any link in `link_path` doesn't exist.

# `robot_module`

```elixir
@spec robot_module(Igniter.t()) :: module()
```

Returns the robot module to operate on.

Resolution order:

  1. The `--robot` option from `igniter.args.options` (parsed module name)
  2. `{AppPrefix}.Robot` (e.g. `MyApp.Robot`)

Add `robot: :string` (and ideally `aliases: [r: :robot]`) to your task's
schema to support the `--robot` flag.

# `set_robot_param_default`

```elixir
@spec set_robot_param_default(Igniter.t(), module(), [atom(), ...], term()) ::
  Igniter.t()
```

Sets a default for a robot parameter in the application config and wires the
robot's child spec to load its opts from the application environment.

`param_path` is the path of the parameter within the robot's `params`, e.g.
`[:config, :feetech, :device]`. The default is written to `config/config.exs`
as a single leaf under `config <app>, <robot_module>, params: [...]`. Writing
a leaf (rather than the whole opts list) lets independent installers each
contribute their own defaults without clobbering one another.

The robot's child spec in the application module is rewritten to
`{<robot_module>, Application.get_env(<app>, <robot_module>, [])}`. If the
existing child opts are already a non-literal expression — e.g. a
`robot_opts()` helper call inserted by a composing installer — they're left
untouched so that switch logic is preserved.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
