# `Grizzly.FirmwareUpdates.OTWUpdateRunner`
[🔗](https://github.com/smartrent/grizzly/blob/v9.1.4/lib/grizzly/firmware_updates/otw_update_runner.ex#L1)

State machine for managing an OTW update for a Z-Wave controller module.

Only 700-series and later modules are supported.

## State Diagram

```mermaid
stateDiagram-v2
  [*] --> startup_delay
  startup_delay --> run_prechecks: delay complete
  run_prechecks --> [*]: no update needed
  run_prechecks --> init: update available
  state if_init <<choice>>
  init --> if_init
  if_init --> reboot_to_bootloader: prechecks ok
  if_init --> detect_bootloader_menu: prechecks failed
  reboot_to_bootloader --> detect_bootloader_menu: menu detected
  detect_bootloader_menu --> [*]: menu not detected
  detect_bootloader_menu --> uploading
  state uploading_if <<choice>>
  uploading --> uploading_if
  uploading_if --> upload_complete: success
  uploading_if --> run_prechecks: first timeout
  uploading_if --> [*]: second timeout
  upload_complete --> [*]
```

# `start_opt`

```elixir
@type start_opt() ::
  {:serial_port, binary()}
  | {:startup_delay, non_neg_integer()}
  | {:upload_timeout, timeout()}
  | {:update_specs, [struct()]}
  | {:module_reset_fun, {module(), atom(), [term()]} | (-&gt; term())}
```

Options for configuring the OTW update runner.

## Options

* `:serial_port` (`t:String.t/0`) - Required. The path to serial port where the Z-Wave module is connected.

* `:startup_delay` (`t:non_neg_integer/0`) - Time to wait after starting the runner before beginning the update process. The default value is `60000`.

* `:upload_timeout` (`t:timeout/0`) - Timeout for the GBL upload. The default value is `90000`.

* `:update_specs` (list of struct of type `Grizzly.FirmwareUpdates.OTW.UpdateSpec`) - A list of `Grizzly.FirmwareUpdates.OTW.UpdateSpec` structs that define the
  available firmware updates. The default value is `[]`.

* `:module_reset_fun` - A function that performs a hard reset of the Z-Wave module, typically via GPIO
  or power cycle. If not provided, recovering from a failed upload may not work
  as expected.

# `state`

```elixir
@type state() ::
  :startup_delay
  | :run_prechecks
  | :init
  | :reboot_to_bootloader
  | :detect_bootloader_menu
  | :uploading
  | :upload_complete
  | :done
```

# `update_status`

```elixir
@type update_status() :: :started | {:done, :success | :skipped} | {:error, atom()}
```

The various status of a firmware update of the Z-Wave module

* `:started` - the firmware update has been initiated and all validation of
  the firmware update is complete.
* `{:done, :success}` - the firmware update of the Z-Wave module is successful.
* `{:done, :skipped}` - no firmware update was applied.
* `{:error, reason}` - A firmware update of the Z-Wave module was attempted
  but failed for some `reason`.

# `busy?`

```elixir
@spec busy?() :: boolean()
```

Returns true if an OTW update is in progress (the state is not `:startup_delay`
or `:done`).

# `child_spec`

```elixir
@spec child_spec(args :: keyword()) :: Supervisor.child_spec()
```

Returns a specification to start this module under a supervisor.

See `Supervisor`.

# `get_state`

```elixir
@spec get_state() :: state() | nil
```

Gets the current state of the OTW update runner process. Returns nil if the
process is not started.

# `start_link`

```elixir
@spec start_link([start_opt()]) :: GenServer.on_start()
```

Start the OTW update runner.

See `t:start_opt/0` for available options.

---

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