diff --git a/lib/towerops/snmp/profiles/dynamic.ex b/lib/towerops/snmp/profiles/dynamic.ex index 60c0a1cd..e94a057f 100644 --- a/lib/towerops/snmp/profiles/dynamic.ex +++ b/lib/towerops/snmp/profiles/dynamic.ex @@ -320,6 +320,9 @@ defmodule Towerops.Snmp.Profiles.Dynamic do # Map numeric state to description, or use the raw value state_descr = Map.get(states, value, "State #{value}") + # Convert states map keys to strings for JSON storage + states_for_json = Map.new(states, fn {k, v} -> {to_string(k), v} end) + %{ sensor_type: "state", sensor_index: "#{sensor_def[:oid_name] || "state"}_#{idx}", @@ -328,7 +331,8 @@ defmodule Towerops.Snmp.Profiles.Dynamic do sensor_unit: "", sensor_divisor: 1, last_value: value / 1, - state_descr: state_descr + state_descr: state_descr, + metadata: %{"states" => states_for_json} } end diff --git a/lib/towerops/snmp/sensor_reading.ex b/lib/towerops/snmp/sensor_reading.ex index 454c63dd..bc24c855 100644 --- a/lib/towerops/snmp/sensor_reading.ex +++ b/lib/towerops/snmp/sensor_reading.ex @@ -16,6 +16,7 @@ defmodule Towerops.Snmp.SensorReading do schema "snmp_sensor_readings" do field :value, :float field :status, :string + field :state_descr, :string field :checked_at, :utc_datetime belongs_to :sensor, Sensor @@ -27,6 +28,7 @@ defmodule Towerops.Snmp.SensorReading do id: Ecto.UUID.t(), value: float() | nil, status: String.t(), + state_descr: String.t() | nil, checked_at: DateTime.t(), sensor_id: Ecto.UUID.t(), sensor: Ecto.Association.NotLoaded.t() | Sensor.t(), @@ -36,7 +38,7 @@ defmodule Towerops.Snmp.SensorReading do @doc false def changeset(reading, attrs) do reading - |> cast(attrs, [:sensor_id, :value, :status, :checked_at]) + |> cast(attrs, [:sensor_id, :value, :status, :state_descr, :checked_at]) |> validate_required([:sensor_id, :status, :checked_at]) |> validate_inclusion(:status, ["ok", "warning", "critical", "error"]) |> foreign_key_constraint(:sensor_id) diff --git a/lib/towerops/workers/device_poller_worker.ex b/lib/towerops/workers/device_poller_worker.ex index 49adcbb9..23bcbb63 100644 --- a/lib/towerops/workers/device_poller_worker.ex +++ b/lib/towerops/workers/device_poller_worker.ex @@ -575,10 +575,14 @@ defmodule Towerops.Workers.DevicePollerWorker do end defp handle_sensor_poll_result(sensor, {:ok, value}, timestamp) do + # For state sensors, map numeric value to state description + state_descr = get_state_description(sensor, value) + Snmp.create_sensor_reading(%{ sensor_id: sensor.id, value: value, status: "ok", + state_descr: state_descr, checked_at: timestamp }) @@ -612,6 +616,15 @@ defmodule Towerops.Workers.DevicePollerWorker do }) end + # Get state description for state sensors from metadata + defp get_state_description(sensor, value) do + if sensor.sensor_type == "state" && is_map(sensor.metadata) do + states = Map.get(sensor.metadata, "states", %{}) + # Convert value to string for lookup (metadata keys are strings) + Map.get(states, to_string(trunc(value)), nil) + end + end + defp log_sensor_error(sensor, :no_such_object) do Logger.debug("Sensor #{sensor.sensor_descr} OID not found on device") end diff --git a/priv/repo/migrations/20260128151151_add_state_descr_to_sensor_readings.exs b/priv/repo/migrations/20260128151151_add_state_descr_to_sensor_readings.exs new file mode 100644 index 00000000..2eb19e03 --- /dev/null +++ b/priv/repo/migrations/20260128151151_add_state_descr_to_sensor_readings.exs @@ -0,0 +1,9 @@ +defmodule Towerops.Repo.Migrations.AddStateDescrToSensorReadings do + use Ecto.Migration + + def change do + alter table(:snmp_sensor_readings) do + add :state_descr, :string + end + end +end