defmodule Grizzly.VirtualDevices.Device do
  @moduledoc """
  Behaviour for implementing virtual device specifics
  """

  alias Grizzly.ZWave.{Command, DeviceClass}

  @typedoc """
  A module that implements this behaviour
  """
  @type t() :: module()

  @typedoc """
  Optional device options that are passed with calling implemented callbacks

  This can be whatever extra information the consumer might need the device to
  know about when executing the callback function.
  """
  @type device_opt() :: {atom(), term()}

  @doc """
  Return the device specification
  """
  @callback device_spec([device_opt()]) :: DeviceClass.t()

  @doc """
  Set the virtual device's id once added to the (virtual) Z-Wave network.
  """
  @callback set_device_id(pid(), Grizzly.VirtualDevices.id()) :: :ok

  @doc """
  Handle a Z-Wave command

  When handling a command you can reply, notify, or do nothing.

  In Z-Wave if your device does not understand the command sent it ignores the
  command. For this case you'd return `{:error, :timeout}`.

  When a command is received and your device supports the command and the
  command's parameters, you either respond with `:ok` or `{:ok, Command.t()}`.
  Normally, when you receive a "set" command you will want to response with
  `:ok`. When you receive a "get" command you will want to response the report
  command like `{:ok, Command.t()}`, where the `Command.t()` is whatever command
  report you want to send to the caller.

  Often times, if your device reports changes that have been made due to
  handling a command, you can return `{:notify, Command.t()}`.
  """
  @callback handle_command(Command.t(), [device_opt()]) ::
              :ok | {:ok, Command.t()} | {:notify, Command.t()} | {:error, :timeout}
end
