fix(insights): raise device overheating thresholds for outdoor equipment
Warning: 65°C → 80°C, Critical: 75°C → 90°C. Outdoor gear in Texas routinely runs at 70-75°C ambient. The old thresholds generated useless AI insights like "Verona to Altoga at 72°C may be overheating" — that's normal for an enclosure in summer, not actionable.
This commit is contained in:
parent
e6e1326689
commit
6f46915eeb
4 changed files with 24 additions and 19 deletions
|
|
@ -121,7 +121,7 @@ defmodule Towerops.LLM.InsightPrompt do
|
|||
""",
|
||||
"device_overheating" => """
|
||||
Rule context: a device has at least one temperature sensor above
|
||||
65 °C (warning) or 75 °C (critical). Metadata fields:
|
||||
80 °C (warning) or 90 °C (critical). Metadata fields:
|
||||
temperature_c, sensor_descr, qoe_score, warning_threshold_c,
|
||||
critical_threshold_c.
|
||||
If qoe_score is also low (<60), highlight that the QoE drop is
|
||||
|
|
|
|||
|
|
@ -187,16 +187,18 @@ defmodule Towerops.LLM.NetworkSnapshot do
|
|||
|> Repo.all()
|
||||
end
|
||||
|
||||
# Devices with at least one temperature sensor reading > 60°C in the
|
||||
# last hour. The `last_value` field on `Sensor` is updated on every
|
||||
# poll, so we don't need a time-series join.
|
||||
# Devices with at least one temperature sensor reading > 80°C.
|
||||
# Outdoor equipment in hot climates routinely hits 70-75°C — we only
|
||||
# surface devices that are approaching thermal-throttling territory.
|
||||
# The `last_value` field on `Sensor` is updated on every poll, so we
|
||||
# don't need a time-series join.
|
||||
defp hottest_devices(organization_id) do
|
||||
Sensor
|
||||
|> join(:inner, [s], sd in SnmpDevice, on: s.snmp_device_id == sd.id)
|
||||
|> join(:inner, [_s, sd], d in Device, on: sd.device_id == d.id)
|
||||
|> where([s, _sd, d], d.organization_id == ^organization_id)
|
||||
|> where([s], s.sensor_type == "temperature")
|
||||
|> where([s], not is_nil(s.last_value) and s.last_value > 60.0)
|
||||
|> where([s], not is_nil(s.last_value) and s.last_value > 80.0)
|
||||
|> order_by([s], desc: s.last_value)
|
||||
|> limit(@top_n)
|
||||
|> select([s, _sd, d], %{device_id: d.id, name: d.name, max_temp_c: s.last_value})
|
||||
|
|
|
|||
|
|
@ -23,8 +23,11 @@ defmodule Towerops.Recommendations.Rules.DeviceOverheating do
|
|||
alias Towerops.Snmp.Device, as: SnmpDevice
|
||||
alias Towerops.Snmp.Sensor
|
||||
|
||||
@warning_threshold_c 65.0
|
||||
@critical_threshold_c 75.0
|
||||
# Outdoor equipment in hot climates routinely runs at 70-75°C ambient.
|
||||
# Thresholds are set at levels where thermal throttling or hardware
|
||||
# damage is actually imminent, not just "warm to the touch."
|
||||
@warning_threshold_c 80.0
|
||||
@critical_threshold_c 90.0
|
||||
@qoe_concern_threshold 60.0
|
||||
# Anything above this is implausible for routing/wireless gear and
|
||||
# almost always indicates a missing divisor in the SNMP profile. Skip
|
||||
|
|
|
|||
|
|
@ -51,21 +51,21 @@ defmodule Towerops.Recommendations.Rules.DeviceOverheatingTest do
|
|||
assert DeviceOverheating.evaluate(org.id) == []
|
||||
end
|
||||
|
||||
test "warning insight at 65–75°C", %{org: org, device: device, snmp: snmp} do
|
||||
insert_temp(snmp, 68.0)
|
||||
test "warning insight at 80–90°C", %{org: org, device: device, snmp: snmp} do
|
||||
insert_temp(snmp, 85.0)
|
||||
|
||||
assert [insight] = DeviceOverheating.evaluate(org.id)
|
||||
assert insight.type == "device_overheating"
|
||||
assert insight.urgency == "warning"
|
||||
assert insight.device_id == device.id
|
||||
assert insight.metadata["temperature_c"] == 68.0
|
||||
assert insight.metadata["temperature_c"] == 85.0
|
||||
assert insight.metadata["sensor_descr"] == "Enclosure"
|
||||
assert insight.title =~ "AP-Hot"
|
||||
assert insight.title =~ "68"
|
||||
assert insight.title =~ "85"
|
||||
end
|
||||
|
||||
test "critical insight at 75°C+", %{org: org, snmp: snmp} do
|
||||
insert_temp(snmp, 78.0)
|
||||
test "critical insight at 90°C+", %{org: org, snmp: snmp} do
|
||||
insert_temp(snmp, 92.0)
|
||||
|
||||
assert [insight] = DeviceOverheating.evaluate(org.id)
|
||||
assert insight.urgency == "critical"
|
||||
|
|
@ -81,17 +81,17 @@ defmodule Towerops.Recommendations.Rules.DeviceOverheatingTest do
|
|||
end
|
||||
|
||||
test "uses the hottest sensor when multiple are present", %{org: org, snmp: snmp} do
|
||||
insert_temp(snmp, 60.0, descr: "CPU")
|
||||
insert_temp(snmp, 70.0, descr: "Enclosure")
|
||||
insert_temp(snmp, 75.0, descr: "CPU")
|
||||
insert_temp(snmp, 88.0, descr: "Enclosure")
|
||||
|
||||
assert [insight] = DeviceOverheating.evaluate(org.id)
|
||||
assert insight.metadata["temperature_c"] == 70.0
|
||||
assert insight.metadata["temperature_c"] == 88.0
|
||||
assert insight.metadata["sensor_descr"] == "Enclosure"
|
||||
end
|
||||
|
||||
test "correlates with Preseem QoE degradation when AP is linked",
|
||||
%{org: org, device: device, snmp: snmp} do
|
||||
insert_temp(snmp, 72.0)
|
||||
insert_temp(snmp, 85.0)
|
||||
|
||||
{:ok, _ap} =
|
||||
%AccessPoint{}
|
||||
|
|
@ -112,7 +112,7 @@ defmodule Towerops.Recommendations.Rules.DeviceOverheatingTest do
|
|||
end
|
||||
|
||||
test "scopes to organization", %{snmp: snmp} do
|
||||
insert_temp(snmp, 78.0)
|
||||
insert_temp(snmp, 85.0)
|
||||
|
||||
other_user = user_fixture()
|
||||
other_org = organization_fixture(other_user.id)
|
||||
|
|
@ -120,7 +120,7 @@ defmodule Towerops.Recommendations.Rules.DeviceOverheatingTest do
|
|||
end
|
||||
|
||||
test "ignores non-temperature sensors", %{org: org, snmp: snmp} do
|
||||
insert_temp(snmp, 78.0, type: "voltage")
|
||||
insert_temp(snmp, 85.0, type: "voltage")
|
||||
assert DeviceOverheating.evaluate(org.id) == []
|
||||
end
|
||||
end
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue