# `ExDlt645.Master`
[🔗](https://github.com/Ultraluv/ex_dlt645/blob/v0.1.0/lib/ex_dlt645/master.ex#L1)

DL/T 645-2007 Master collector GenServer.

Issues commands to one or more electricity meters over physical serial ports
(`Circuits.UART`), in-memory virtual buses (`ExDlt645.VirtualBus`), or TCP sockets.

Serializes requests to ensure half-duplex RS-485 bus safety with `:busy` protection.
Supports both synchronous (passive) and asynchronous event-driven (active) modes.

# `master_ref`

```elixir
@type master_ref() :: pid() | atom()
```

# `child_spec`

Returns a specification to start this module under a supervisor.

See `Supervisor`.

# `close`

```elixir
@spec close(master_ref()) :: :ok
```

Closes the transport port.

# `custom_request`

```elixir
@spec custom_request(master_ref(), binary(), keyword()) ::
  {:ok, ExDlt645.Frame.t()} | {:error, term()}
```

Sends a raw DL/T 645 packet.

# `freeze`

```elixir
@spec freeze(master_ref(), String.t(), binary(), keyword()) :: :ok | {:error, term()}
```

Sends a freeze command (0x16) to meter `address`.

# `open`

```elixir
@spec open(master_ref()) :: :ok | {:error, term()}
```

Opens the transport port for the configured device.

# `read_address`

```elixir
@spec read_address(master_ref(), keyword()) :: {:ok, String.t()} | {:error, term()}
```

Reads meter address using broadcast frame (0x13).
Typically only one meter should be attached to the bus when invoking this.

# `read_data`

```elixir
@spec read_data(
  master_ref(),
  String.t(),
  non_neg_integer() | binary() | String.t(),
  keyword()
) ::
  {:ok, term()} | {:error, term()}
```

Reads a data item identified by `di` from meter `address`.
Returns `{:ok, parsed_value}` or `{:ok, %Frame{}}` if `raw: true`.

# `start_link`

```elixir
@spec start_link(keyword(), keyword()) :: {:ok, pid()} | {:error, term()}
```

Starts the Master GenServer process.

## Parameters
- `:tty` - Serial device path (e.g. `"/dev/ttyUSB0"`), virtual target (`"virtual"`), or TCP spec (`"tcp://192.168.1.100:10001"`).
- `:active` - If `true`, asynchronous notifications are delivered to caller PID (default `false`).
- `:timeout` - Request timeout in milliseconds (default 5,000 ms).
- `:transport` - Optional explicit Transport module (defaults to resolving based on `:tty`).
- `:uart_opts` - Options passed to UART or virtual transport (e.g. `[speed: 2400, parity: :even]`).

# `state`

```elixir
@spec state(master_ref()) :: %ExDlt645.Master{
  active: term(),
  cmd: term(),
  last_cmd: term(),
  parent_pid: term(),
  status: term(),
  timeout: term(),
  transport: term(),
  tty: term(),
  uart_opts: term(),
  uart_pid: term()
}
```

Returns the current internal state struct of the Master.

# `stop`

```elixir
@spec stop(master_ref()) :: :ok
```

Stops the Master GenServer process.

# `sync_time`

```elixir
@spec sync_time(master_ref(), NaiveDateTime.t() | DateTime.t(), keyword()) ::
  :ok | {:error, term()}
```

Broadcasts time synchronization frame (0x08).

# `write_address`

```elixir
@spec write_address(master_ref(), String.t(), String.t(), keyword()) ::
  :ok | {:error, term()}
```

Writes a new address to meter `old_address`.

# `write_data`

```elixir
@spec write_data(
  master_ref(),
  String.t(),
  non_neg_integer() | binary(),
  binary(),
  keyword()
) ::
  :ok | {:ok, ExDlt645.Frame.t()} | {:error, term()}
```

Writes data to meter `address`.

---

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