# `Jido.Sensor`
[🔗](https://github.com/agentjido/jido/blob/v2.3.3/lib/jido/sensor.ex#L1)

Defines the behaviour and metadata macro for Sensors in the Jido system.

A Sensor is a pure behaviour that transforms external events into Signals.
Sensors are stateless modules that define how to initialize, handle events,
and emit signals. Runtime execution is handled by `Jido.Sensor.Runtime`.

## Usage

To define a new Sensor, use the `Jido.Sensor` behaviour in your module:

    defmodule MySensor do
      use Jido.Sensor,
        name: "my_sensor",
        description: "Monitors a specific metric",
        schema: Zoi.object(%{
          metric: Zoi.string()
        })

      @impl true
      def init(config, _context) do
        {:ok, %{metric: config.metric, last_value: nil}}
      end

      @impl true
      def handle_event({:metric_update, value}, state) do
        signal = Jido.Signal.new!(%{
          source: "my_sensor",
          type: "metric.updated",
          data: %{value: value, previous: state.last_value}
        })
        {:ok, %{state | last_value: value}, [{:emit, signal}]}
      end
    end

## Callbacks

Implementing modules must define:

- `c:init/2`: Initialize sensor state from config and context
- `c:handle_event/2`: Process incoming events and emit signals

Optional callbacks:

- `c:terminate/2`: Called when the sensor is shutting down (default: `:ok`)

## Directives

Callbacks can return directives to request runtime actions:

- `{:schedule, interval}` - Schedule next poll after interval ms
- `{:schedule, interval, payload}` - Schedule with custom payload
- `{:emit, signal}` - Emit a signal immediately

Sensor modules that need external subscriptions or connections should establish
them in `init/2` and clean them up in `terminate/2`.

# `sensor_directive`

```elixir
@type sensor_directive() ::
  {:schedule, pos_integer()}
  | {:schedule, pos_integer(), term()}
  | {:emit, Jido.Signal.t()}
```

# `handle_event`

```elixir
@callback handle_event(event :: term(), state :: term()) ::
  {:ok, state :: term()}
  | {:ok, state :: term(), directives :: [sensor_directive()]}
  | {:error, reason :: term()}
```

Handle an incoming event and produce directives.

Called when the sensor receives an event from its connected source(s).
Should return directives describing what actions to take.

## Parameters

- `event` - The incoming event (format depends on the source)
- `state` - Current sensor state

## Returns

- `{:ok, state}` - Updated state with no directives
- `{:ok, state, directives}` - Updated state with directives to execute
- `{:error, reason}` - Event handling failed

## Directives

- `{:emit, signal}` - Emit a signal to the connected agent
- `{:schedule, interval_ms}` - Schedule a `:tick` event after interval
- `{:schedule, interval_ms, event}` - Schedule a custom event after interval

## Example

    def handle_event(:tick, state) do
      signal = Jido.Signal.new!(%{source: "/sensor/example", type: "example.tick"})
      {:ok, state, [{:emit, signal}, {:schedule, 1000}]}
    end

# `init`

```elixir
@callback init(config :: map(), context :: map()) ::
  {:ok, state :: term()}
  | {:ok, state :: term(), directives :: [sensor_directive()]}
  | {:error, reason :: term()}
```

Initialize the sensor state.

Called when the sensor is started. Receives the validated configuration
and a context map containing runtime information.

## Parameters

- `config` - Validated configuration map (parsed against the sensor's schema)
- `context` - Runtime context (e.g., sensor id, parent process info)

## Returns

- `{:ok, state}` - Initial state for the sensor
- `{:ok, state, directives}` - Initial state with startup directives
- `{:error, reason}` - Initialization failed

# `terminate`

```elixir
@callback terminate(reason :: term(), state :: term()) :: :ok
```

Called when the sensor is shutting down.

Use this to clean up any resources. The default implementation returns `:ok`.

## Parameters

- `reason` - The shutdown reason
- `state` - Current sensor state

## Returns

- `:ok` - Shutdown complete

