Skip to content

Commit 85f5990

Browse files
committed
feat: add temperature treshold
1 parent 303e325 commit 85f5990

6 files changed

Lines changed: 99 additions & 2 deletions

File tree

apps/auditing/events.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,5 @@
99
{"identifier": "ADMIN_LOGIN", "name": "Выполнен вход в панель администратора"},
1010
{"identifier": "DASHBOARD_VIEWED", "name": "Просмотрена панель мониторинга"},
1111
{"identifier": "DOS_DETECTED", "name": "Обнаружена DoS-атака"},
12+
{"identifier": "TEMP_THRESHOLD_EXCEEDED", "name": "Превышен порог температуры"},
1213
]

apps/sensor/admin.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,13 @@ class SensorConfigAdmin(ModelAdmin, SingletonModelAdmin):
1212
"id",
1313
"period",
1414
"server_timeout",
15+
"temperature_threshold",
1516
"log_rotation_days",
1617
"log_rotation_max_count",
1718
"device_pruning_days",
1819
)
1920
fieldsets = (
20-
("Настройки датчиков", {"fields": ("period", "server_timeout")}),
21+
("Настройки датчиков", {"fields": ("period", "server_timeout", "temperature_threshold")}),
2122
(
2223
"Ротация данных",
2324
{
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Generated by Django 5.2.6 on 2025-09-13 10:00
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
('sensor', '0003_alter_temperaturereading_timestamp'),
10+
]
11+
12+
operations = [
13+
migrations.AddField(
14+
model_name='sensorconfig',
15+
name='temperature_threshold',
16+
field=models.FloatField(blank=True, help_text='Если указано, будет отправлено уведомление при превышении этой температуры. Оставьте пустым, чтобы отключить.', null=True, verbose_name='Пороговое значение температуры (°C)'),
17+
),
18+
]
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Generated by Django 5.2.7 on 2025-10-16 09:56
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
('sensor', '0004_sensorconfig_temperature_threshold'),
10+
]
11+
12+
operations = [
13+
migrations.AlterField(
14+
model_name='sensorconfig',
15+
name='server_timeout',
16+
field=models.PositiveIntegerField(default=5, help_text='Запретить отправку данных чаще, чем раз в указанное количество секунд с одного IP. Например, значение 5 разрешит 1 запрос каждые 5 секунд. 0 - отключить ограничение.', verbose_name='Таймаут на стороне сервера'),
17+
),
18+
]

apps/sensor/models.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,12 @@ class SensorConfig(SingletonModel):
2323
default=5,
2424
help_text="Запретить отправку данных чаще, чем раз в указанное количество секунд с одного IP. Например, значение 5 разрешит 1 запрос каждые 5 секунд. 0 - отключить ограничение.",
2525
)
26+
temperature_threshold = models.FloatField(
27+
verbose_name="Пороговое значение температуры (°C)",
28+
null=True,
29+
blank=True,
30+
help_text="Если указано, будет отправлено уведомление при превышении этой температуры. Оставьте пустым, чтобы отключить.",
31+
)
2632

2733
# Data Rotation Settings
2834
log_rotation_days = models.PositiveIntegerField(

apps/sensor/views.py

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,9 +116,58 @@ class TemperatureDataAPIView(generics.CreateAPIView):
116116
serializer_class = TemperatureReadingSerializer
117117
throttle_classes = [DynamicSensorDataRateThrottle]
118118

119+
def _check_temperature_threshold(
120+
self, reading: TemperatureReading, ip_address: str, device: Device
121+
) -> None:
122+
"""
123+
Check if a temperature reading exceeds the configured threshold and log an event.
124+
125+
Args:
126+
reading: The TemperatureReading instance to check.
127+
ip_address: The IP address from which the reading was received.
128+
device: The Device associated with the reading.
129+
"""
130+
try:
131+
config = SensorConfig.get_solo()
132+
threshold = config.temperature_threshold
133+
134+
if threshold is None:
135+
return
136+
137+
temps = [reading.contact_temp, reading.non_contact_temp]
138+
valid_temps = [t for t in temps if t is not None]
139+
140+
if not valid_temps:
141+
return
142+
143+
max_temp = max(valid_temps)
144+
145+
if max_temp > threshold:
146+
event_logged.send(
147+
sender=self.__class__,
148+
event_identifier="TEMP_THRESHOLD_EXCEEDED",
149+
device=device,
150+
instance=reading,
151+
details={
152+
"ip_address": ip_address,
153+
"threshold": threshold,
154+
"measured_temp": max_temp,
155+
"contact_temp": reading.contact_temp,
156+
"non_contact_temp": reading.non_contact_temp,
157+
},
158+
)
159+
except SensorConfig.DoesNotExist:
160+
# If config does not exist, we cannot check the threshold.
161+
# This is unlikely with django-solo but handled for safety.
162+
pass
163+
119164
def perform_create(self, serializer: TemperatureReadingSerializer) -> None:
120165
"""
121-
Save the new TemperatureReading instance and log the event.
166+
Save the new TemperatureReading instance, log events, and check for alerts.
167+
168+
This method saves the reading, logs the standard DATA_RECEIVED event,
169+
and then checks if the temperature exceeds the globally configured
170+
threshold, triggering a TEMP_THRESHOLD_EXCEEDED event if necessary.
122171
123172
Args:
124173
serializer: The validated serializer instance containing the data.
@@ -128,6 +177,7 @@ def perform_create(self, serializer: TemperatureReadingSerializer) -> None:
128177

129178
instance = serializer.save(device=device)
130179

180+
# Log the standard data reception event
131181
event_logged.send(
132182
sender=self.__class__,
133183
event_identifier="DATA_RECEIVED",
@@ -136,6 +186,9 @@ def perform_create(self, serializer: TemperatureReadingSerializer) -> None:
136186
details={"ip_address": ip_address, "payload": serializer.validated_data},
137187
)
138188

189+
# Check for temperature threshold alert
190+
self._check_temperature_threshold(instance, ip_address, device)
191+
139192

140193
@extend_schema(
141194
tags=["sensor"],

0 commit comments

Comments
 (0)