This commit is contained in:
ferdzo
2024-10-10 12:58:04 +02:00
parent 48a4967e30
commit 56935db4d5
9 changed files with 519 additions and 145 deletions

View File

@@ -1,26 +1,14 @@
from django import forms
from .models import Device, Sensor, SensorType
class DeviceForm(forms.ModelForm):
# Optionally include sensors as choices in the form if relevant
sensors = forms.ModelMultipleChoiceField(
queryset=Sensor.objects.all(),
required=False,
widget=forms.CheckboxSelectMultiple,
label='Sensors'
)
class DeviceForm(forms.ModelForm):
class Meta:
model = Device
fields = ['name', 'ip', 'protocol']
fields = ['name', 'ip', 'protocol'] # Exclude sensors from the fields
def __init__(self, *args, **kwargs):
# Optionally pass initial sensors for editing an existing device
if 'instance' in kwargs:
initial_sensors = kwargs['instance'].sensors.all() if kwargs['instance'] else None
initial = kwargs.get('initial', {})
initial['sensors'] = initial_sensors
kwargs['initial'] = initial
# No need to handle sensors in the form
super(DeviceForm, self).__init__(*args, **kwargs)
def save(self, commit=True):
@@ -29,7 +17,6 @@ class DeviceForm(forms.ModelForm):
if commit:
device.save()
self.save_m2m() # Ensure M2M save happens
return device
class SensorWithTypeForm(forms.ModelForm):
@@ -45,7 +32,11 @@ class SensorWithTypeForm(forms.ModelForm):
class Meta:
model = Sensor
fields = ['device', 'enabled']
fields = ['enabled'] # Exclude 'device' from the form fields
def __init__(self, *args, **kwargs):
self.device = kwargs.pop('device', None) # Get the device from kwargs
super(SensorWithTypeForm, self).__init__(*args, **kwargs)
def save(self, commit=True):
# Create or get the SensorType
@@ -65,8 +56,9 @@ class SensorWithTypeForm(forms.ModelForm):
# Create Sensor with the SensorType found or created
sensor = super(SensorWithTypeForm, self).save(commit=False)
sensor.type = sensor_type
sensor.device = self.device # Associate the sensor with the device
if commit:
sensor.save()
return sensor
return sensor

View File

@@ -11,6 +11,7 @@ from .models import Device, Sensor, SensorType
# Initialize Redis client
redis_client = redis.StrictRedis(host='10.10.0.1', port=6379, db=0)
def devices_to_redis():
"""Fetch devices and their sensors' topics from Django and store them in Redis."""
devices = Device.objects.all()
@@ -26,6 +27,7 @@ def devices_to_redis():
redis_client.set('mqtt_devices', json.dumps(devices_list))
print("Devices with sensors stored in Redis.")
def fetch_data_http(device, sensor):
"""Fetch data from an HTTP sensor."""
sensor_type_name = sensor.type.name.lower()
@@ -47,6 +49,7 @@ def fetch_data_http(device, sensor):
print(f"HTTP request failed for {device.name}: {e}")
return None
def fetch_data_mqtt(device, sensor):
"""Fetch data from Redis for a specific MQTT device and sensor."""
# Get the data for the specific device from Redis
@@ -70,9 +73,10 @@ def fetch_data_mqtt(device, sensor):
def is_recent_data(timestamp):
"""Check if data is within a 2-minute freshness window."""
"""Check if data is within a 1-minute freshness window."""
data_time = datetime.datetime.fromisoformat(timestamp)
return data_time > datetime.datetime.utcnow() - datetime.timedelta(minutes=2)
return data_time > datetime.datetime.utcnow() - datetime.timedelta(minutes=1)
def insert_data(data, sensor_type):
"""Insert parsed data into the PostgreSQL database."""
@@ -105,7 +109,8 @@ def insert_data(data, sensor_type):
except Exception as e:
print(f"Failed to insert data: {e}")
@periodi c_task(crontab(minute='*/1'))
@periodic_task(crontab(minute='*/1'))
def fetch_data_from_all_devices():
"""Fetch and insert data for all devices based on their protocol."""
devices = Device.objects.all()
@@ -123,6 +128,7 @@ def fetch_data_from_all_devices():
else:
print(f"No recent or valid data for {device.name}. Skipping.")
@periodic_task(crontab(minute='*/5'))
def last_5_minutes():
"""Fetch the last 5 readings from TimescaleDB and store them in Redis."""
@@ -151,5 +157,6 @@ def last_5_minutes():
except Exception as e:
print(f"Error fetching or storing the last 5 readings: {e}")
# Initialize device data in Redis
devices_to_redis()

View File

@@ -3,142 +3,280 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>IoT Sensor Dashboard</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/css/bootstrap.min.css" rel="stylesheet">
<title>IoT Dashboard</title>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
<script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datetimepicker/4.17.47/css/bootstrap-datetimepicker.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datetimepicker/4.17.47/js/bootstrap-datetimepicker.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/raphael/2.3.0/raphael.min.js"></script> <!-- Include Raphael.js -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/justgage/1.4.0/justgage.min.js"></script> <!-- Include JustGage.js -->
<style>
.device-panel {
.navbar {
margin-bottom: 20px;
}
.sensor-card {
margin-top: 20px;
}
.gpt-section {
margin-top: 20px;
background-color: #f8f9fa;
padding: 20px;
border-radius: 10px;
}
.chart-container {
position: relative;
height: 300px;
margin-bottom: 20px; /* Space between charts */
}
.footer {
margin-top: 40px;
padding: 20px;
background-color: #343a40;
color: white;
text-align: center;
}
.date-picker {
margin-bottom: 20px;
}
</style>
</head>
<body class="container my-4">
<!-- GPT Summary -->
<h4 class="text-center mb-4">{{ gpt }}</h4>
<body>
<h1 class="text-center mb-4">IoT Sensor Data Dashboard</h1>
<!-- Button to add a new "island" (device panel) -->
<div class="text-center mb-4">
<button class="btn btn-success" onclick="addDevicePanel()">Add Device Panel</button>
<!-- Navigation Bar -->
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<a class="navbar-brand" href="{% url 'index' %}">IoT Dashboard</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav ml-auto">
<li class="nav-item"><a class="nav-link" href="{% url 'device_list' %}">Devices</a></li>
<li class="nav-item"><a class="nav-link" href="{% url 'add_sensor_with_type' %}">Sensors</a></li>
<li class="nav-item"><a class="nav-link" href="/admin/">Logout</a></li>
</ul>
</div>
</nav>
<!-- Container for dynamic device panels ("islands") -->
<div id="device-panels" class="row">
<!-- Dynamic panels will be added here -->
<!-- Page Title -->
<div class="container text-center">
<h2>GPT Summary from All the Environment's Sensors</h2>
</div>
<!-- GPT Recommendations Section -->
<div class="container gpt-section">
<div class="row">
<div class="col-md-8">
<h5>Modular Sensor Graphs with Current Parameters</h5>
<!-- Date Pickers for selecting start and end dates -->
<div class="row date-picker">
<div class="col-md-5">
<label for="startDate">Start Date and Time:</label>
<input type="text" id="startDate" class="form-control datetimepicker-input" data-toggle="datetimepicker" data-target="#startDate"/>
</div>
<div class="col-md-5">
<label for="endDate">End Date and Time:</label>
<input type="text" id="endDate" class="form-control datetimepicker-input" data-toggle="datetimepicker" data-target="#endDate"/>
</div>
<div class="col-md-2">
<label>&nbsp;</label>
<button id="applyDateRange" class="btn btn-primary btn-block">Apply</button>
</div>
</div>
<!-- Sensor Chart -->
<div id="sensor-charts" class="chart-container">
<canvas id="sensorChart"></canvas>
</div>
<!-- Button to add a new chart -->
<button id="addChartBtn" class="btn btn-success">Add New Chart</button>
</div>
<div class="col-md-4">
<h5>GPT Recommendations for Parameters</h5>
<p id="gpt-summary"></p>
</div>
</div>
</div>
<script>
const devices = {{ devices_json|safe }};
let panelCount = 0;
const charts = {};
<!-- Device Cards Section -->
<div class="container sensor-card">
<div class="row" id="device-list">
<!-- Device cards will be dynamically inserted here -->
</div>
</div>
// Function to add a new device panel
function addDevicePanel() {
const panelId = `device-panel-${panelCount}`;
const chartId = `sensorChart-${panelCount}`;
const panelHtml = `
<div class="col-md-6 device-panel" id="${panelId}">
<!-- Footer -->
<div class="footer">
<p>&copy; 2024 IoT Dashboard. All rights reserved.</p>
</div>
<script>
$(document).ready(function() {
// Initialize the date pickers
$('#startDate').datetimepicker({ format: 'YYYY-MM-DD HH:mm:ss' });
$('#endDate').datetimepicker({ format: 'YYYY-MM-DD HH:mm:ss' });
// Load device cards
var devices = JSON.parse('{{ devices_json | safe }}');
devices.forEach(device => {
$('#device-list').append(`
<div class="col-md-4">
<div class="card">
<div class="card-header d-flex justify-content-between">
<span>Device Panel</span>
<button class="btn btn-danger btn-sm" onclick="removeDevicePanel('${panelId}')">Remove</button>
</div>
<div class="card-body">
<div class="mb-3">
<label for="device-select-${panelId}" class="form-label">Choose a device:</label>
<select id="device-select-${panelId}" class="form-select" onchange="fetchSensorData('${panelId}', '${chartId}')">
<!-- Dynamically populated options -->
</select>
</div>
<!-- Date and Time Selection -->
<div class="mb-3">
<label for="start-date-${panelId}" class="form-label">Start Date and Time:</label>
<input type="datetime-local" id="start-date-${panelId}" class="form-control">
</div>
<div class="mb-3">
<label for="end-date-${panelId}" class="form-label">End Date and Time:</label>
<input type="datetime-local" id="end-date-${panelId}" class="form-control">
</div>
<button class="btn btn-primary w-100" onclick="fetchSensorData('${panelId}', '${chartId}')">Fetch Data</button>
<canvas id="${chartId}" class="mt-4"></canvas>
<h5 class="card-title">${device.name}</h5>
<p class="card-text">Sensor Type: ${device.sensors__type__name}</p>
</div>
</div>
</div>`;
</div>
`);
});
document.getElementById('device-panels').insertAdjacentHTML('beforeend', panelHtml);
populateDeviceSelect(`device-select-${panelId}`);
// GPT Summary
var gptData = {{ gpt | safe }}; // Assuming the GPT data is passed from the server
$('#gpt-summary').text(gptData.summary);
const ctx = document.getElementById(chartId).getContext('2d');
charts[chartId] = new Chart(ctx, {
type: 'line',
data: { labels: [], datasets: [] },
options: { responsive: true }
// Update recommendations
function updateRecommendations() {
const recommendations = gptData.recommendations;
Object.keys(recommendations).forEach(key => {
$('#gpt-summary').append(`<br>${key}: ${recommendations[key]}`);
});
panelCount++; // Increment panelCount after using it
}
updateRecommendations();
// Function to populate device dropdown
function populateDeviceSelect(selectId) {
const deviceSelect = document.getElementById(selectId);
deviceSelect.innerHTML = ""; // Clear previous options
devices.forEach(device => {
const option = document.createElement('option');
option.value = device.name;
option.text = `${device.name} (${device.sensors__type__name})`;
deviceSelect.appendChild(option);
// Fetch sensor data and update the chart dynamically
function updateChart(deviceName, startDate, endDate) {
$.ajax({
url: "{% url 'fetch_device_data' %}",
data: {
device: deviceName,
start_date: startDate,
end_date: endDate
},
success: function(response) {
// Clear any existing chart data
sensorChart.data.labels = [];
sensorChart.data.datasets = [];
// Loop through the response to extract times and values for each metric
for (var metric in response) {
sensorChart.data.labels = response[metric].times;
sensorChart.data.datasets.push({
label: metric,
data: response[metric].values,
borderColor: 'rgba(75, 192, 192, 1)',
fill: false
});
}
// Update the chart with the new data
sensorChart.update();
},
error: function(xhr, status, error) {
console.error("Failed to fetch sensor data:", error);
}
});
}
// Function to remove a device panel
function removeDevicePanel(panelId) {
const panel = document.getElementById(panelId);
panel.remove();
}
// Function to fetch sensor data and update the chart for a specific panel
function fetchSensorData(panelId, chartId) {
const device = document.getElementById(`device-select-${panelId}`).value;
const startDate = document.getElementById(`start-date-${panelId}`).value;
const endDate = document.getElementById(`end-date-${panelId}`).value;
if (!device || !startDate || !endDate) {
alert('Please select a device and both start and end date/time.');
return;
// Initialize Chart.js with empty data
var ctx = document.getElementById('sensorChart').getContext('2d');
var sensorChart = new Chart(ctx, {
type: 'line',
data: {
labels: [], // Labels will be dynamically loaded
datasets: [] // Datasets will be dynamically loaded
},
options: {
responsive: true,
title: {
display: true,
text: 'Sensor Data Over Time'
},
scales: {
x: {
display: true,
title: {
display: true,
text: 'Time'
}
},
y: {
display: true,
title: {
display: true,
text: 'Value'
}
}
}
}
});
fetch(`/fetch_device_data?device=${device}&start_date=${startDate}&end_date=${endDate}`)
.then(response => response.json())
.then(data => {
const times = Object.values(data)[0].times;
const datasets = Object.keys(data).map(metric => ({
label: metric,
data: data[metric].values,
borderColor: getRandomColor(),
fill: false
}));
// Function to apply date range and fetch new data
$('#applyDateRange').click(function() {
var startDate = $('#startDate').val();
var endDate = $('#endDate').val();
var deviceName = 'Livingroom'; // Default device for now, update this as needed
charts[chartId].data.labels = times;
charts[chartId].data.datasets = datasets;
charts[chartId].update();
});
// Call the updateChart function with the selected dates
updateChart(deviceName, startDate, endDate);
});
// Fetch initial data for default device
updateChart('Livingroom', null, null);
// Function to dynamically add a new chart
$('#addChartBtn').click(function() {
var newChartId = 'chart' + Date.now(); // Unique ID for each chart
addNewChart(newChartId, {
labels: ['Jan', 'Feb', 'Mar', 'Apr'],
datasets: [{
label: 'New Sensor Data',
data: [30, 60, 90, 120],
borderColor: 'rgba(75, 192, 192, 1)',
fill: false
}]
});
});
// Function to dynamically add a new chart
function addNewChart(chartId, chartData) {
var canvas = document.createElement('canvas');
canvas.id = chartId;
document.getElementById('sensor-charts').appendChild(canvas);
var ctx = document.getElementById(chartId).getContext('2d');
new Chart(ctx, {
type: 'line',
data: chartData,
options: {
responsive: true,
title: {
display: true,
text: 'Dynamic Sensor Data Over Time'
},
scales: {
x: {
display: true,
title: {
display: true,
text: 'Time'
}
},
y: {
display: true,
title: {
display: true,
text: 'Value'
}
}
}
}
});
}
// Function to generate random color for chart lines
function getRandomColor() {
const letters = '0123456789ABCDEF';
let color = '#';
for (let i = 0; i < 6; i++) color += letters[Math.floor(Math.random() * 16)];
return color;
}
// Initialize with one device panel
addDevicePanel();
</script>
});
</script>
</body>
</html>

View File

@@ -46,7 +46,9 @@
{% csrf_token %}
<div class="mb-3">
<!-- Display the form fields -->
{{ form.as_p }}
{{ form.name.label_tag }} {{ form.name }}<br>
{{ form.ip.label_tag }} {{ form.ip }}<br>
{{ form.protocol.label_tag }} {{ form.protocol }}<br>
<!-- If there are errors, display them -->
{% if form.errors %}
@@ -64,6 +66,14 @@
<button type="submit" class="btn btn-success">Save</button>
<a href="{% url 'device_list' %}" class="btn btn-secondary">Cancel</a>
</form>
<!-- Links for adding/editing sensors for this specific device -->
<div class="mt-4">
{% if form.instance.pk %}
<a href="{% url 'add_sensor' form.instance.pk %}" class="btn btn-primary">Add Sensor</a>
<a href="{% url 'sensor_list' form.instance.pk %}" class="btn btn-info">Edit Sensors</a>
{% endif %}
</div>
</div>
</body>
</html>

View File

@@ -0,0 +1,48 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Confirm Delete Sensor</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body class="bg-light">
<!-- Navbar -->
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<div class="container-fluid">
<a class="navbar-brand" href="#">IoT Dashboard</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item"><a class="nav-link" href="{% url 'index' %}">Chart</a></li>
<li class="nav-item"><a class="nav-link" href="{% url 'device_list' %}">Devices</a></li>
</ul>
<ul class="navbar-nav">
{% if user.is_authenticated %}
<li class="nav-item"><a class="nav-link" href="{% url 'logout' %}">Logout</a></li>
{% else %}
<li class="nav-item"><a class="nav-link" href="{% url 'login' %}">Login</a></li>
{% endif %}
</ul>
</div>
</div>
</nav>
<!-- Delete Confirmation -->
<div class="container mt-5">
<h1 class="text-center mb-4">Confirm Delete Sensor</h1>
<div class="alert alert-warning">
<strong>Warning!</strong> Are you sure you want to delete the sensor "{{ sensor.type.name }}"? This action cannot be undone.
</div>
<form method="POST">
{% csrf_token %}
<div class="text-center">
<button type="submit" class="btn btn-danger">Delete</button>
<a href="{% url 'sensor_list' sensor.device.pk %}" class="btn btn-secondary">Cancel</a>
</div>
</form>
</div>
</body>
</html>

View File

@@ -23,6 +23,7 @@
<form method="post">
{% csrf_token %}
{{ form.as_p }} <!-- Renders the inputs for all fields you added in your form -->
<input type="hidden" name="device_id" value="{{ device.id }}"> <!-- Hidden field for device ID -->
<button type="submit" class="btn btn-success">Save</button>
<a href="{% url 'device_list' %}" class="btn btn-secondary">Cancel</a>
</form>
@@ -38,4 +39,4 @@
{% endif %}
</div>
</body>
</html>
</html>

View File

@@ -0,0 +1,64 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Manage Sensors for {{ device.name }}</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body class="bg-light">
<!-- Navbar -->
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<div class="container-fluid">
<a class="navbar-brand" href="#">IoT Dashboard</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item"><a class="nav-link" href="{% url 'index' %}">Chart</a></li>
<li class="nav-item"><a class="nav-link" href="{% url 'device_list' %}">Devices</a></li>
</ul>
<ul class="navbar-nav">
{% if user.is_authenticated %}
<li class="nav-item"><a class="nav-link" href="{% url 'logout' %}">Logout</a></li>
{% else %}
<li class="nav-item"><a class="nav-link" href="{% url 'login' %}">Login</a></li>
{% endif %}
</ul>
</div>
</div>
</nav>
<!-- Sensor Management -->
<div class="container mt-5">
<h1>Sensors for {{ device.name }}</h1>
<table class="table">
<thead>
<tr>
<th>Sensor Type</th>
<th>Enabled</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{% for sensor in sensors %}
<tr>
<td>{{ sensor.type.name }}</td>
<td>{{ sensor.enabled }}</td>
<td>
<a href="{% url 'edit_sensor' sensor.pk %}" class="btn btn-warning btn-sm">Edit</a>
<a href="{% url 'delete_sensor' sensor.pk %}" class="btn btn-danger btn-sm">Delete</a>
</td>
</tr>
{% empty %}
<tr>
<td colspan="3">No sensors found for this device.</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</body>
</html>

View File

@@ -20,13 +20,18 @@ from iotDashboard import views
urlpatterns = [
path('admin/', admin.site.urls),
path('devices_api/',views.devices_api),
path('',views.chart,name="index"),
path('devices_api/', views.devices_api),
path('', views.chart, name="index"),
path('fetch_device_data/', views.fetch_device_data, name='fetch_device_data'),
path('devices/', views.device_list, name='device_list'),
path('devices/add/', views.add_device, name='add_device'),
path('devices/edit/<int:pk>/', views.edit_device, name='edit_device'),
path('devices/delete/<int:pk>/', views.delete_device, name='delete_device'),
path('logout/', views.logout_view, name='logout'),
path('sensor/add/',views.add_sensor_with_type,name="add_sensor_with_type")
path('sensors/<int:device_id>/', views.sensor_list, name='sensor_list'),
path('sensor/add/', views.add_sensor_with_type, name="add_sensor_with_type"),
# path('devices/<int:device_id>/sensors/', views.sensor_list, name='sensor_list'),
path('device/<int:device_id>/add_sensor/', views.add_sensor, name='add_sensor'),
path('sensor/edit/<int:pk>/', views.edit_sensor, name='edit_sensor'),
path('sensor/delete/<int:pk>/', views.delete_sensor, name='delete_sensor'),
]

View File

@@ -1,14 +1,13 @@
import json
from django.core.serializers.json import DjangoJSONEncoder
from django.http import JsonResponse, HttpResponse
from django.db import connections
from django.shortcuts import render, redirect, get_object_or_404
from django.views.decorators.http import require_GET
from .models import Device, Sensor, SensorType
from .forms import DeviceForm, SensorWithTypeForm
import redis
from django.core.serializers.json import DjangoJSONEncoder
from django.db import connections
from django.http import JsonResponse, HttpResponse
from django.shortcuts import render, redirect, get_object_or_404
from .forms import DeviceForm, SensorWithTypeForm
from .models import Device, Sensor
redis_client = redis.StrictRedis(host='10.10.0.1', port=6379, db=0)
@@ -27,18 +26,78 @@ def chart(request):
# Pass devices data to the context
gpt = fetch_gpt_data()
gpt = json.loads(gpt)
context = {'devices_json': devices_json, 'gpt': gpt["summary"]}
context = {'devices_json': devices_json, 'gpt': gpt}
return render(request, 'chart.html', context)
# Fetch sensor data (AJAX)
# def fetch_device_data(request):
# device_name = request.GET.get('device', 'Livingroom')
# start_date = request.GET.get('start_date')
# end_date = request.GET.get('end_date')
#
# # Log the parameters to ensure they are correct
# print("Device Name:", device_name)
# print("Start Date:", start_date)
# print("End Date:", end_date)
#
# # Get the specific device by name
# device = get_object_or_404(Device, name=device_name)
#
# # Initialize the results dictionary to store sensor data
# results = {}
#
# # Prepare SQL query and parameters for the specific sensor type
# query = """
# SELECT time, metric, value
# FROM sensor_readings
# WHERE device_name = %s
# """
# params = [device.name]
#
# # Add time filtering to the query
# if start_date:
# query += " AND time >= %s::timestamptz"
# params.append(start_date)
#
# if end_date:
# query += " AND time <= %s::timestamptz"
# params.append(end_date)
#
# # Log the final query and params
# print("Final Query:", query)
# print("Params Before Execution:", params)
#
# # Fetch data from the database
# with connections["data"].cursor() as cursor:
# cursor.execute(query, params)
# rows = cursor.fetchall()
#
# # Process the results and group them by sensor type (metric)
# for row in rows:
# time, metric, value = row
# formatted_time = time.strftime('%Y-%m-%d %H:%M:%S')
#
# if metric not in results:
# results[metric] = {
# 'times': [],
# 'values': []
# }
# results[metric]['times'].append(formatted_time)
# results[metric]['values'].append(value)
#
# return JsonResponse(results)
def fetch_device_data(request):
device_name = request.GET.get('device', 'Livingroom')
sensor_name = request.GET.get('sensor') # Optional parameter for a specific sensor
start_date = request.GET.get('start_date')
end_date = request.GET.get('end_date')
# Log the parameters to ensure they are correct
print("Device Name:", device_name)
print("Sensor Name:", sensor_name) # Log sensor name
print("Start Date:", start_date)
print("End Date:", end_date)
@@ -48,7 +107,7 @@ def fetch_device_data(request):
# Initialize the results dictionary to store sensor data
results = {}
# Prepare SQL query and parameters for the specific sensor type
# Prepare SQL query and parameters for the device
query = """
SELECT time, metric, value
FROM sensor_readings
@@ -56,6 +115,11 @@ def fetch_device_data(request):
"""
params = [device.name]
# If a specific sensor is specified, filter by that sensor
if sensor_name:
query += " AND metric = %s"
params.append(sensor_name)
# Add time filtering to the query
if start_date:
query += " AND time >= %s::timestamptz"
@@ -89,6 +153,7 @@ def fetch_device_data(request):
return JsonResponse(results)
def index(request):
if request.user.is_authenticated:
return redirect("/chart/")
@@ -130,6 +195,7 @@ def delete_device(request, pk):
return redirect('device_list')
return render(request, 'device_confirm_delete.html', {'device': device})
def add_sensor_with_type(request):
if request.method == 'POST':
form = SensorWithTypeForm(request.POST)
@@ -142,10 +208,53 @@ def add_sensor_with_type(request):
context = {'form': form}
return render(request, 'sensor_form.html', context)
def logout_view(request):
return redirect("/admin")
def devices_api(request):
devices = list(Device.objects.all().values('name', 'sensors__type__name'))
return JsonResponse(devices, safe=False)
def sensor_list(request, device_id):
device = get_object_or_404(Device, id=device_id)
sensors = device.sensors.all() # Get sensors for this specific device
return render(request, 'sensor_list.html', {'device': device, 'sensors': sensors})
def edit_sensor(request, pk):
sensor = get_object_or_404(Sensor, pk=pk)
if request.method == 'POST':
form = SensorWithTypeForm(request.POST, instance=sensor)
if form.is_valid():
form.save()
return redirect('sensor_list', device_id=sensor.device.pk)
else:
form = SensorWithTypeForm(instance=sensor)
return render(request, 'sensor_form.html', {'form': form})
def delete_sensor(request, pk):
sensor = get_object_or_404(Sensor, pk=pk)
if request.method == 'POST':
device_id = sensor.device.pk
sensor.delete()
return redirect('sensor_list', device_id=device_id)
return render(request, 'sensor_confirm_delete.html', {'sensor': sensor})
def add_sensor(request, device_id):
device = get_object_or_404(Device, pk=device_id)
if request.method == 'POST':
form = SensorWithTypeForm(request.POST)
if form.is_valid():
sensor = form.save(commit=False)
sensor.device = device # Associate the sensor with the device
sensor.save()
return redirect('device_list') # Redirect to device list or appropriate view
else:
form = SensorWithTypeForm()
return render(request, 'sensor_form.html', {'form': form, 'device': device})