diff --git a/lib/towerops/workers/device_poller_worker.ex b/lib/towerops/workers/device_poller_worker.ex index 23bcbb63..01cc4bf2 100644 --- a/lib/towerops/workers/device_poller_worker.ex +++ b/lib/towerops/workers/device_poller_worker.ex @@ -493,7 +493,10 @@ defmodule Towerops.Workers.DevicePollerWorker do end defp handle_state_sensor_poll_result(state_sensor, {:ok, new_state_value}, timestamp, device_id) do + old_state_value = state_sensor.state_value + old_state_descr = state_sensor.state_descr old_status = state_sensor.status + new_status = entity_state_to_status(new_state_value) new_descr = entity_state_to_descr(new_state_value) @@ -504,8 +507,19 @@ defmodule Towerops.Workers.DevicePollerWorker do last_checked_at: timestamp }) - if old_status != new_status do - broadcast_state_sensor_change(state_sensor, old_status, new_status, device_id, timestamp) + # Track any state value change, not just status changes + # This catches changes like "6X (64QAM)" -> "8X (256QAM)" that have the same status + if old_state_value != nil && old_state_value != new_state_value do + state_changes = %{ + old_state_value: old_state_value, + new_state_value: new_state_value, + old_state_descr: old_state_descr, + new_state_descr: new_descr, + old_status: old_status, + new_status: new_status + } + + broadcast_state_sensor_change(state_sensor, state_changes, device_id, timestamp) end end @@ -518,19 +532,36 @@ defmodule Towerops.Workers.DevicePollerWorker do }) end - defp broadcast_state_sensor_change(state_sensor, old_status, new_status, device_id, timestamp) do + defp broadcast_state_sensor_change(state_sensor, state_changes, device_id, timestamp) do + %{ + old_state_value: old_state_value, + new_state_value: new_state_value, + old_state_descr: old_state_descr, + new_state_descr: new_state_descr, + old_status: old_status, + new_status: new_status + } = state_changes + severity = determine_state_change_severity(old_status, new_status) event_type = determine_state_change_event_type(new_status) + # Format message like: "RX Modulation Rate changed from 6X (64QAM)(6) to 8X (256QAM)(8)" + message = + "#{state_sensor.sensor_descr} changed from #{old_state_descr}(#{old_state_value}) to #{new_state_descr}(#{new_state_value})" + event = %{ device_id: device_id, event_type: event_type, severity: severity, - message: "#{state_sensor.sensor_descr} status changed from #{old_status} to #{new_status}", + message: message, metadata: %{ state_sensor_id: state_sensor.id, sensor_name: state_sensor.sensor_descr, entity_type: state_sensor.entity_type, + old_state_value: old_state_value, + new_state_value: new_state_value, + old_state_descr: old_state_descr, + new_state_descr: new_state_descr, old_status: old_status, new_status: new_status }, diff --git a/lib/towerops_web/live/device_live/show.ex b/lib/towerops_web/live/device_live/show.ex index a1ad4332..b037488b 100644 --- a/lib/towerops_web/live/device_live/show.ex +++ b/lib/towerops_web/live/device_live/show.ex @@ -788,18 +788,23 @@ defmodule ToweropsWeb.DeviceLive.Show do end # Sensor type classification helpers - # These check multiple signals (type, unit, description) to correctly categorize sensors - # since some devices (especially Mikrotik) report incorrect unit types + # Trust sensor_type first (set during SNMP discovery based on OID) + # Only fall back to unit/description matching if sensor_type is missing/unknown defp temperature_sensor?(sensor) do + # Primary: Check sensor_type (set during discovery based on OID) type_match = sensor.sensor_type in ["temperature", "cpu_temperature", "celsius"] + + # Fallback: Check unit for devices that don't set sensor_type correctly unit_match = sensor.sensor_unit in ["°C", "C", "celsius"] + # Only use description as last resort if type and unit don't match + # and only for unambiguous temperature keywords descr_match = - sensor.sensor_descr && - String.contains?(String.downcase(sensor.sensor_descr), ["temperature", "temp"]) && + !type_match && !unit_match && + sensor.sensor_descr && + String.contains?(String.downcase(sensor.sensor_descr), "temperature") && !String.contains?(String.downcase(sensor.sensor_descr), "voltage") - # Match by type OR unit, but exclude if description says "voltage" (type_match || unit_match || descr_match) && !voltage_by_description?(sensor) end