📊 Веб-интерфейсы для датчиков. Превращаем данные IoT в инсайты и действия
📋 Паспорт спринта
| Параметр | Значение |
|---|---|
| Предмет | Интернет вещей (элективный курс) |
| Класс | 9 класс |
| Спринт № | 22 из 36 |
| Тип занятия | Продвинутая веб-разработка с data science элементами |
| Продолжительность | 90 минут |
| Формат | Интенсивная разработка + анализ данных |
🎯 Цели спринта (Sprint Goals)
Основная цель:
Создать профессиональные веб-интерфейсы для мониторинга и анализа данных IoT датчиков с элементами предиктивной аналитики и автоматизации
Конкретные результаты спринта:
- Учащиеся интегрируют множественные датчики в единый дашборд
- Реализуют historical data storage и visualization
- Создают системы алертов и уведомлений на основе данных датчиков
- Применяют базовые алгоритмы анализа данных (тренды, аномалии)
- Разрабатывают автоматические действия на основе sensor data
- 🆕 Создают экспортируемые отчеты и дашборды для stakeholders
- 🆕 Реализуют predictive maintenance и smart automation
🔄 Sprint Retrospective предыдущего спринта (0-3 мин)
Быстрая проверка домашнего задания:
- “Поднимите руку, кто запустил Flask дашборд дома на своем Pi?”
- “Кто показал свой интерфейс родителям? Какая была реакция?”
- 🆕 “Какую самую интересную проблему пришлось решать в веб-разработке?”
- “Кто добавил функции, которых не было в задании?”
Связка с новой темой: “Отлично! Вы создали красивые интерфейсы. Но настоящая сила IoT - в анализе данных и автоматизации. Сегодня превращаем ваши дашборды в умные системы!”
🕐 Sprint Timeline (90 минут)
⚡ SPRINT START (3-8 мин): Активация через “data story”
Задача: Показать мощь data-driven решений в IoT через впечатляющий кейс
🆕 Демо-эксперимент “Предсказание будущего”:
- Учитель показывает готовый дашборд с историческими данными температуры за неделю
- Демонстрирует trend analysis - “Смотрите, каждый день в 14:00 температура поднимается”
- Показывает prediction - “Завтра в 14:00 будет 26°C, включим кондиционер заранее”
- 🆕 Триггерит автоматическое действие - дашборд отправляет команду включить вентилятор
- Показывает alert system - “Влажность критично низкая, отправляю SMS родителям”
- 🆕 Экспортирует PDF отчет - “Недельная сводка для управляющей компании”
Wow-момент: “Это не просто мониторинг - это предиктивная IoT система, которая думает наперед!”
🆕 Формулировка миссии: “Сегодня вы становитесь Data Scientists IoT! Ваша задача - научить системы предсказывать проблемы и решать их автоматически!”
📚 THEORY BLOCK (8-25 мин): От данных к инсайтам
Микро-блок 1 (8-13 мин): Data Pipeline в IoT системах
1🔄 IOT DATA PIPELINE:
2
3📡 СБОР (Collection):
4├── Sensors → Raw Data (temperature: 23.4°C, timestamp)
5├── Sampling Rate (каждые 5 секунд vs каждый час)
6├── Data Quality (проверка на валидность)
7└── Edge Processing (предварительная обработка на устройстве)
8
9💾 ХРАНЕНИЕ (Storage):
10├── Time-series Database (InfluxDB, TimescaleDB)
11├── SQL vs NoSQL для IoT data
12├── Data Retention Policies (хранить месяц vs год)
13└── Compression и Optimization
14
15🔍 ОБРАБОТКА (Processing):
16├── Real-time Processing (streaming data)
17├── Batch Processing (анализ за период)
18├── Data Cleaning (удаление выбросов)
19└── Feature Engineering (создание новых метрик)
20
21📊 АНАЛИЗ (Analytics):
22├── Descriptive: Что происходило? (дашборды, отчеты)
23├── Diagnostic: Почему это случилось? (корреляции)
24├── Predictive: Что случится? (machine learning)
25└── Prescriptive: Что делать? (автоматизация)
26
27🎯 ДЕЙСТВИЯ (Actions):
28├── Alerts и Notifications
29├── Automated Controls (включить/выключить)
30├── Reports для людей
31└── API для других систем
🆕 Аналогия с врачебной диагностикой:
- Sensors = медицинские приборы (термометр, тонометр)
- Data Collection = регулярные осмотры
- Analytics = диагностика (найти проблему)
- Predictions = профилактика (предотвратить болезнь)
- Actions = лечение (назначить процедуры)
Микро-блок 2 (13-18 мин): Продвинутая веб-визуализация
1📈 ADVANCED VISUALIZATION FOR IOT:
2
3⏰ TIME-SERIES CHARTS:
4├── Line Charts: Температура за день/неделю/месяц
5├── Area Charts: Заполнение под кривой (энергопотребление)
6├── Multi-axis: Температура + влажность на одном графике
7└── Zoom & Pan: Детальный просмотр периодов
8
9📊 REAL-TIME DASHBOARDS:
10├── Gauge Meters: Текущие значения с цветовой индикацией
11├── Status Indicators: Online/Offline, OK/Warning/Critical
12├── Sparklines: Мини-графики трендов в таблицах
13└── Heat Maps: Данные по времени и местоположению
14
15🎛️ INTERACTIVE CONTROLS:
16├── Time Range Selectors: Последний час/день/неделя
17├── Filter Controls: По типу датчика, локации, статусу
18├── Drill-down Navigation: От общего к детальному
19└── Export Functions: PDF, CSV, PNG отчеты
20
21🚨 ALERTING & NOTIFICATIONS:
22├── Threshold-based: Температура > 30°C
23├── Trend-based: Влажность падает последние 2 часа
24├── Anomaly Detection: Необычные паттерны
25└── Multi-channel: Email, SMS, Push, Telegram
26
27🎨 RESPONSIVE & ACCESSIBLE:
28├── Mobile-first Design: Finger-friendly на телефонах
29├── Color-blind Friendly: Не только цвет для статусов
30├── High Contrast Mode: Для слабовидящих
31└── Progressive Enhancement: Работает без JavaScript
Микро-блок 3 (18-25 мин): Smart Automation & ML
1🤖 УМНАЯ АВТОМАТИЗАЦИЯ:
2
3🧠 RULE-BASED AUTOMATION:
4├── IF температура > 25°C THEN включить вентилятор
5├── IF влажность < 30% AND время = утро THEN полить растения
6├── IF движение обнаружено AND время = ночь THEN включить свет
7└── Комбинированные правила с AND/OR логикой
8
9📈 PREDICTIVE AUTOMATION:
10├── Trend Analysis: "Температура растет, включим кондиционер заранее"
11├── Pattern Recognition: "По вторникам всегда жарко в 14:00"
12├── Seasonal Adjustments: "Зимой нужно больше света утром"
13└── Learning from History: "Прошлый раз это помогло"
14
15🔍 ANOMALY DETECTION:
16├── Statistical Methods: Значения вне 2-3 стандартных отклонений
17├── Machine Learning: Изоляционный лес, One-class SVM
18├── Domain Knowledge: Физически невозможные значения
19└── Contextual Anomalies: Нормально днем, странно ночью
20
21⚡ REAL-TIME DECISION MAKING:
22├── Stream Processing: Решения за миллисекунды
23├── Edge Computing: Локальные решения без интернета
24├── Failsafe Mechanisms: Что делать если связь пропала
25└── Human Override: Возможность отменить автоматику
26
27📊 BUSINESS INTELLIGENCE:
28├── KPI Dashboards: Ключевые показатели эффективности
29├── Cost Analysis: Сколько сэкономили на электричестве
30├── Performance Reports: Эффективность автоматизации
31└── ROI Calculation: Окупаемость IoT инвестиций
☕ BREAK (25-30 мин): Техническая пауза
🛠️ ПРАКТИЧЕСКИЙ БЛОК (30-75 мин): “IoT Analytics Lab” - Умные системы
Этап 1: Формирование Analytics Teams (30-35 мин)
4 команды аналитиков:
- 🔵 Blue Analytics: “Predictive Climate Control” - предиктивное управление климатом
- 🔴 Red Analytics: “Energy Optimization” - оптимизация энергопотребления
- 🟢 Green Analytics: “Agricultural Intelligence” - сельскохозяйственная аналитика
- 🟡 Yellow Analytics: “Facility Management” - управление зданием
🆕 Роли в каждой команде:
- Data Engineer - создает pipeline сбора и хранения данных
- Frontend Analyst - разрабатывает аналитические интерфейсы
- ML Specialist - реализует алгоритмы предсказания и аномалий
- Business Analyst - создает отчеты и KPI для руководства
Этап 2: Enhanced Database Schema (35-40 мин)
🆕 Универсальная схема для всех команд:
1# enhanced_database.py
2import sqlite3
3import json
4from datetime import datetime, timedelta
5import random
6import math
7
8class IoTDataManager:
9 def __init__(self, db_name='iot_analytics.db'):
10 self.db_name = db_name
11 self.init_database()
12 self.populate_historical_data()
13
14 def init_database(self):
15 conn = sqlite3.connect(self.db_name)
16 cursor = conn.cursor()
17
18 # Основная таблица данных датчиков
19 cursor.execute('''
20 CREATE TABLE IF NOT EXISTS sensor_data (
21 id INTEGER PRIMARY KEY AUTOINCREMENT,
22 sensor_id TEXT NOT NULL,
23 sensor_type TEXT NOT NULL,
24 location TEXT NOT NULL,
25 value REAL NOT NULL,
26 unit TEXT NOT NULL,
27 timestamp DATETIME NOT NULL,
28 quality_score REAL DEFAULT 1.0,
29 metadata TEXT
30 )
31 ''')
32
33 # Таблица событий и алертов
34 cursor.execute('''
35 CREATE TABLE IF NOT EXISTS events (
36 id INTEGER PRIMARY KEY AUTOINCREMENT,
37 event_type TEXT NOT NULL,
38 sensor_id TEXT,
39 description TEXT NOT NULL,
40 severity TEXT NOT NULL,
41 timestamp DATETIME NOT NULL,
42 resolved BOOLEAN DEFAULT FALSE,
43 resolution_notes TEXT
44 )
45 ''')
46
47 # Таблица автоматических действий
48 cursor.execute('''
49 CREATE TABLE IF NOT EXISTS automation_actions (
50 id INTEGER PRIMARY KEY AUTOINCREMENT,
51 action_type TEXT NOT NULL,
52 trigger_condition TEXT NOT NULL,
53 target_device TEXT NOT NULL,
54 action_data TEXT NOT NULL,
55 timestamp DATETIME NOT NULL,
56 success BOOLEAN DEFAULT TRUE,
57 error_message TEXT
58 )
59 ''')
60
61 # Таблица конфигурации правил
62 cursor.execute('''
63 CREATE TABLE IF NOT EXISTS automation_rules (
64 id INTEGER PRIMARY KEY AUTOINCREMENT,
65 rule_name TEXT NOT NULL,
66 condition TEXT NOT NULL,
67 action TEXT NOT NULL,
68 enabled BOOLEAN DEFAULT TRUE,
69 created_date DATETIME NOT NULL,
70 last_triggered DATETIME
71 )
72 ''')
73
74 conn.commit()
75 conn.close()
76
77 def populate_historical_data(self):
78 """Генерация исторических данных за последние 7 дней"""
79 conn = sqlite3.connect(self.db_name)
80 cursor = conn.cursor()
81
82 # Проверяем, есть ли уже данные
83 cursor.execute("SELECT COUNT(*) FROM sensor_data")
84 if cursor.fetchone()[0] > 100:
85 conn.close()
86 return
87
88 sensors = [
89 {'id': 'temp_01', 'type': 'temperature', 'location': 'living_room', 'unit': 'C'},
90 {'id': 'hum_01', 'type': 'humidity', 'location': 'living_room', 'unit': '%'},
91 {'id': 'temp_02', 'type': 'temperature', 'location': 'bedroom', 'unit': 'C'},
92 {'id': 'light_01', 'type': 'light', 'location': 'living_room', 'unit': 'lux'},
93 {'id': 'motion_01', 'type': 'motion', 'location': 'hallway', 'unit': 'bool'},
94 {'id': 'power_01', 'type': 'power', 'location': 'total', 'unit': 'kW'},
95 ]
96
97 start_time = datetime.now() - timedelta(days=7)
98
99 for sensor in sensors:
100 for i in range(7 * 24 * 12): # Каждые 5 минут в течение недели
101 timestamp = start_time + timedelta(minutes=5 * i)
102
103 # Генерация реалистичных данных
104 value = self.generate_realistic_value(sensor, timestamp)
105
106 cursor.execute('''
107 INSERT INTO sensor_data
108 (sensor_id, sensor_type, location, value, unit, timestamp, quality_score)
109 VALUES (?, ?, ?, ?, ?, ?, ?)
110 ''', (
111 sensor['id'], sensor['type'], sensor['location'],
112 value, sensor['unit'], timestamp,
113 random.uniform(0.95, 1.0) # Высокое качество данных
114 ))
115
116 conn.commit()
117 conn.close()
118
119 def generate_realistic_value(self, sensor, timestamp):
120 """Генерация реалистичных значений с учетом времени суток и типа датчика"""
121 hour = timestamp.hour
122 day_of_week = timestamp.weekday()
123
124 if sensor['type'] == 'temperature':
125 # Базовая температура с суточными колебаниями
126 base_temp = 22
127 daily_variation = 3 * math.sin(2 * math.pi * (hour - 6) / 24)
128 noise = random.uniform(-0.5, 0.5)
129 return round(base_temp + daily_variation + noise, 1)
130
131 elif sensor['type'] == 'humidity':
132 # Влажность обратно коррелирует с температурой
133 base_humidity = 50
134 daily_variation = -2 * math.sin(2 * math.pi * (hour - 6) / 24)
135 noise = random.uniform(-3, 3)
136 return max(20, min(80, round(base_humidity + daily_variation + noise, 1)))
137
138 elif sensor['type'] == 'light':
139 # Освещенность зависит от времени суток
140 if 6 <= hour <= 18:
141 base_light = 500 + 300 * math.sin(math.pi * (hour - 6) / 12)
142 else:
143 base_light = random.uniform(0, 50) # Искусственное освещение
144 return round(base_light + random.uniform(-50, 50))
145
146 elif sensor['type'] == 'motion':
147 # Движение более вероятно днем и вечером
148 if 7 <= hour <= 23:
149 probability = 0.3 if day_of_week < 5 else 0.5 # Больше дома в выходные
150 else:
151 probability = 0.05 # Редко ночью
152 return 1 if random.random() < probability else 0
153
154 elif sensor['type'] == 'power':
155 # Энергопотребление зависит от активности
156 base_power = 1.5 # Базовая нагрузка
157 if 6 <= hour <= 9 or 18 <= hour <= 23:
158 activity_power = random.uniform(2, 5) # Высокая активность
159 else:
160 activity_power = random.uniform(0.5, 2) # Низкая активность
161 return round(base_power + activity_power, 2)
162
163 return random.uniform(0, 100) # Fallback
Этап 3: Специализированные Analytics по командам (40-65 мин)
🔵 BLUE ANALYTICS: “Predictive Climate Control”
1# blue_analytics.py - Климатическая аналитика
2from flask import Flask, render_template, jsonify, request
3import sqlite3
4import pandas as pd
5import numpy as np
6from datetime import datetime, timedelta
7from sklearn.linear_model import LinearRegression
8import json
9
10app = Flask(__name__)
11
12class ClimateAnalytics:
13 def __init__(self):
14 self.db = IoTDataManager()
15 self.comfort_zone = {'temp_min': 20, 'temp_max': 24, 'hum_min': 40, 'hum_max': 60}
16
17 def get_comfort_analysis(self, hours=24):
18 """Анализ комфортности за период"""
19 conn = sqlite3.connect(self.db.db_name)
20
21 # Получение данных температуры и влажности
22 query = '''
23 SELECT timestamp, sensor_type, value
24 FROM sensor_data
25 WHERE sensor_type IN ('temperature', 'humidity')
26 AND timestamp > datetime('now', '-{} hours')
27 ORDER BY timestamp
28 '''.format(hours)
29
30 df = pd.read_sql_query(query, conn)
31 conn.close()
32
33 if df.empty:
34 return {'comfort_score': 0, 'recommendations': []}
35
36 # Pivot для удобства анализа
37 df_pivot = df.pivot_table(
38 index='timestamp',
39 columns='sensor_type',
40 values='value',
41 aggfunc='mean'
42 ).fillna(method='ffill')
43
44 # Расчет комфортности
45 comfort_scores = []
46 for _, row in df_pivot.iterrows():
47 temp_comfort = self.calculate_temp_comfort(row.get('temperature', 22))
48 hum_comfort = self.calculate_humidity_comfort(row.get('humidity', 50))
49 overall_comfort = (temp_comfort + hum_comfort) / 2
50 comfort_scores.append(overall_comfort)
51
52 avg_comfort = np.mean(comfort_scores)
53
54 # Рекомендации
55 recommendations = self.generate_climate_recommendations(df_pivot)
56
57 return {
58 'comfort_score': round(avg_comfort, 1),
59 'recommendations': recommendations,
60 'hourly_comfort': list(zip(
61 df_pivot.index.strftime('%H:%M').tolist(),
62 [round(s, 1) for s in comfort_scores]
63 ))
64 }
65
66 def calculate_temp_comfort(self, temp):
67 """Расчет комфортности температуры (0-10)"""
68 if self.comfort_zone['temp_min'] <= temp <= self.comfort_zone['temp_max']:
69 return 10
70 elif temp < self.comfort_zone['temp_min']:
71 return max(0, 10 - 2 * (self.comfort_zone['temp_min'] - temp))
72 else:
73 return max(0, 10 - 2 * (temp - self.comfort_zone['temp_max']))
74
75 def calculate_humidity_comfort(self, humidity):
76 """Расчет комфортности влажности (0-10)"""
77 if self.comfort_zone['hum_min'] <= humidity <= self.comfort_zone['hum_max']:
78 return 10
79 elif humidity < self.comfort_zone['hum_min']:
80 return max(0, 10 - 0.2 * (self.comfort_zone['hum_min'] - humidity))
81 else:
82 return max(0, 10 - 0.2 * (humidity - self.comfort_zone['hum_max']))
83
84 def generate_climate_recommendations(self, df):
85 """Генерация рекомендаций по климату"""
86 recommendations = []
87
88 latest_data = df.iloc[-1] if not df.empty else {}
89 current_temp = latest_data.get('temperature', 22)
90 current_humidity = latest_data.get('humidity', 50)
91
92 # Температурные рекомендации
93 if current_temp > self.comfort_zone['temp_max']:
94 recommendations.append({
95 'type': 'cooling',
96 'priority': 'high',
97 'message': f'Температура {current_temp}°C выше комфортной. Рекомендуется включить кондиционер или вентилятор.',
98 'action': 'turn_on_ac'
99 })
100 elif current_temp < self.comfort_zone['temp_min']:
101 recommendations.append({
102 'type': 'heating',
103 'priority': 'high',
104 'message': f'Температура {current_temp}°C ниже комфортной. Рекомендуется включить отопление.',
105 'action': 'turn_on_heater'
106 })
107
108 # Рекомендации по влажности
109 if current_humidity > self.comfort_zone['hum_max']:
110 recommendations.append({
111 'type': 'dehumidify',
112 'priority': 'medium',
113 'message': f'Влажность {current_humidity}% слишком высокая. Включите осушитель или вентиляцию.',
114 'action': 'turn_on_dehumidifier'
115 })
116 elif current_humidity < self.comfort_zone['hum_min']:
117 recommendations.append({
118 'type': 'humidify',
119 'priority': 'medium',
120 'message': f'Влажность {current_humidity}% слишком низкая. Включите увлажнитель.',
121 'action': 'turn_on_humidifier'
122 })
123
124 return recommendations
125
126 def predict_temperature(self, hours_ahead=6):
127 """Простое предсказание температуры на основе тренда"""
128 conn = sqlite3.connect(self.db.db_name)
129
130 query = '''
131 SELECT timestamp, value
132 FROM sensor_data
133 WHERE sensor_type = 'temperature'
134 AND timestamp > datetime('now', '-48 hours')
135 ORDER BY timestamp
136 '''
137
138 df = pd.read_sql_query(query, conn)
139 conn.close()
140
141 if len(df) < 10:
142 return None
143
144 # Подготовка данных для ML
145 df['timestamp_numeric'] = pd.to_datetime(df['timestamp']).astype('int64') // 10**9
146 X = df[['timestamp_numeric']].values
147 y = df['value'].values
148
149 # Обучение модели
150 model = LinearRegression()
151 model.fit(X, y)
152
153 # Предсказание
154 future_time = datetime.now() + timedelta(hours=hours_ahead)
155 future_timestamp = int(future_time.timestamp())
156 predicted_temp = model.predict([[future_timestamp]])[0]
157
158 return {
159 'predicted_temperature': round(predicted_temp, 1),
160 'prediction_time': future_time.strftime('%H:%M'),
161 'confidence': 'medium' # Упрощенная оценка
162 }
163
164@app.route('/api/climate-analysis')
165def climate_analysis():
166 analytics = ClimateAnalytics()
167 analysis = analytics.get_comfort_analysis()
168 prediction = analytics.predict_temperature()
169
170 return jsonify({
171 'comfort_analysis': analysis,
172 'temperature_prediction': prediction,
173 'timestamp': datetime.now().isoformat()
174 })
175
176@app.route('/api/automation-trigger', methods=['POST'])
177def automation_trigger():
178 """Триггер автоматических действий на основе рекомендаций"""
179 data = request.get_json()
180 action = data.get('action')
181
182 # Здесь бы был код управления реальными устройствами
183 automation_log = {
184 'action': action,
185 'timestamp': datetime.now().isoformat(),
186 'trigger': 'climate_recommendation',
187 'success': True
188 }
189
190 # Логирование в базу данных
191 conn = sqlite3.connect('iot_analytics.db')
192 cursor = conn.cursor()
193 cursor.execute('''
194 INSERT INTO automation_actions
195 (action_type, trigger_condition, target_device, action_data, timestamp)
196 VALUES (?, ?, ?, ?, ?)
197 ''', (
198 action, 'climate_recommendation', 'climate_control',
199 json.dumps(data), datetime.now()
200 ))
201 conn.commit()
202 conn.close()
203
204 return jsonify(automation_log)
🔴 RED ANALYTICS: “Energy Optimization”
1# red_analytics.py - Энергетическая аналитика
2class EnergyAnalytics:
3 def __init__(self):
4 self.db = IoTDataManager()
5 self.energy_rate = 0.05 # $0.05 per kWh
6
7 def get_energy_consumption_analysis(self, days=7):
8 """Подробный анализ энергопотребления"""
9 conn = sqlite3.connect(self.db.db_name)
10
11 query = '''
12 SELECT timestamp, value
13 FROM sensor_data
14 WHERE sensor_type = 'power'
15 AND timestamp > datetime('now', '-{} days')
16 ORDER BY timestamp
17 '''.format(days)
18
19 df = pd.read_sql_query(query, conn)
20 conn.close()
21
22 if df.empty:
23 return {}
24
25 df['timestamp'] = pd.to_datetime(df['timestamp'])
26 df['hour'] = df['timestamp'].dt.hour
27 df['day_of_week'] = df['timestamp'].dt.dayofweek
28 df['date'] = df['timestamp'].dt.date
29
30 # Расчет потребления по часам
31 hourly_avg = df.groupby('hour')['value'].mean()
32
33 # Расчет потребления по дням недели
34 daily_avg = df.groupby('day_of_week')['value'].mean()
35
36 # Общее потребление и стоимость
37 total_kwh = df['value'].sum() / 12 # Данные каждые 5 минут
38 total_cost = total_kwh * self.energy_rate
39
40 # Поиск пиков потребления
41 peak_hours = hourly_avg.nlargest(3)
42
43 # Рекомендации по оптимизации
44 optimization_recommendations = self.generate_energy_recommendations(df, hourly_avg)
45
46 return {
47 'total_consumption_kwh': round(total_kwh, 2),
48 'total_cost_usd': round(total_cost, 2),
49 'average_power_kw': round(df['value'].mean(), 2),
50 'peak_hours': {hour: round(power, 2) for hour, power in peak_hours.items()},
51 'hourly_pattern': hourly_avg.round(2).to_dict(),
52 'daily_pattern': daily_avg.round(2).to_dict(),
53 'optimization_potential': optimization_recommendations
54 }
55
56 def generate_energy_recommendations(self, df, hourly_avg):
57 """Генерация рекомендаций по энергосбережению"""
58 recommendations = []
59
60 # Анализ пиковых часов
61 peak_threshold = hourly_avg.mean() + hourly_avg.std()
62 peak_hours = hourly_avg[hourly_avg > peak_threshold].index.tolist()
63
64 if peak_hours:
65 peak_hours_str = ', '.join([f"{h:02d}:00" for h in peak_hours])
66 potential_savings = sum(hourly_avg[h] - hourly_avg.mean() for h in peak_hours) * self.energy_rate * 30
67
68 recommendations.append({
69 'type': 'peak_optimization',
70 'priority': 'high',
71 'message': f'Пиковое потребление в часы: {peak_hours_str}',
72 'potential_savings_monthly': round(potential_savings, 2),
73 'suggestions': [
74 'Перенести использование энергоемких приборов на непиковые часы',
75 'Использовать таймеры для автоматического управления',
76 'Рассмотреть возможность установки аккумуляторов'
77 ]
78 })
79
80 # Анализ базового потребления
81 night_consumption = hourly_avg[0:6].mean() # 00:00-06:00
82 if night_consumption > 2.0: # Более 2 кВт ночью
83 recommendations.append({
84 'type': 'standby_optimization',
85 'priority': 'medium',
86 'message': f'Высокое ночное потребление: {night_consumption:.1f} кВт',
87 'potential_savings_monthly': round((night_consumption - 1.5) * 6 * 30 * self.energy_rate, 2),
88 'suggestions': [
89 'Проверить устройства в режиме ожидания',
90 'Использовать smart plugs для полного отключения',
91 'Настроить расписания работы приборов'
92 ]
93 })
94
95 return recommendations
96
97 def detect_energy_anomalies(self):
98 """Обнаружение аномалий в энергопотреблении"""
99 conn = sqlite3.connect(self.db.db_name)
100
101 query = '''
102 SELECT timestamp, value
103 FROM sensor_data
104 WHERE sensor_type = 'power'
105 AND timestamp > datetime('now', '-24 hours')
106 ORDER BY timestamp
107 '''
108
109 df = pd.read_sql_query(query, conn)
110 conn.close()
111
112 if len(df) < 50:
113 return []
114
115 # Простое обнаружение аномалий через статистику
116 mean_power = df['value'].mean()
117 std_power = df['value'].std()
118 threshold = mean_power + 2 * std_power
119
120 anomalies = df[df['value'] > threshold]
121
122 anomaly_events = []
123 for _, row in anomalies.iterrows():
124 anomaly_events.append({
125 'timestamp': row['timestamp'],
126 'power_value': round(row['value'], 2),
127 'expected_range': f"{mean_power - std_power:.1f} - {mean_power + std_power:.1f} кВт",
128 'severity': 'high' if row['value'] > threshold * 1.5 else 'medium'
129 })
130
131 return anomaly_events
132
133@app.route('/api/energy-analysis')
134def energy_analysis():
135 analytics = EnergyAnalytics()
136 consumption_data = analytics.get_energy_consumption_analysis()
137 anomalies = analytics.detect_energy_anomalies()
138
139 return jsonify({
140 'consumption_analysis': consumption_data,
141 'anomalies': anomalies,
142 'timestamp': datetime.now().isoformat()
143 })
Этап 4: Advanced Frontend с Chart.js (65-75 мин)
🆕 Универсальный продвинутый template:
1<!-- templates/analytics_dashboard.html -->
2{% extends "base.html" %}
3{% block title %}IoT Analytics Dashboard{% endblock %}
4
5{% block content %}
6<div class="container-fluid">
7 <!-- KPI Cards Row -->
8 <div class="row mb-4">
9 <div class="col-md-3">
10 <div class="card bg-primary text-white">
11 <div class="card-body">
12 <div class="d-flex justify-content-between">
13 <div>
14 <h4 id="total-sensors">--</h4>
15 <p class="mb-0">Active Sensors</p>
16 </div>
17 <div class="align-self-center">
18 <i class="fas fa-microchip fa-2x"></i>
19 </div>
20 </div>
21 </div>
22 </div>
23 </div>
24
25 <div class="col-md-3">
26 <div class="card bg-success text-white">
27 <div class="card-body">
28 <div class="d-flex justify-content-between">
29 <div>
30 <h4 id="system-health">--</h4>
31 <p class="mb-0">System Health</p>
32 </div>
33 <div class="align-self-center">
34 <i class="fas fa-heartbeat fa-2x"></i>
35 </div>
36 </div>
37 </div>
38 </div>
39 </div>
40
41 <div class="col-md-3">
42 <div class="card bg-warning text-white">
43 <div class="card-body">
44 <div class="d-flex justify-content-between">
45 <div>
46 <h4 id="active-alerts">--</h4>
47 <p class="mb-0">Active Alerts</p>
48 </div>
49 <div class="align-self-center">
50 <i class="fas fa-exclamation-triangle fa-2x"></i>
51 </div>
52 </div>
53 </div>
54 </div>
55 </div>
56
57 <div class="col-md-3">
58 <div class="card bg-info text-white">
59 <div class="card-body">
60 <div class="d-flex justify-content-between">
61 <div>
62 <h4 id="data-points">--</h4>
63 <p class="mb-0">Data Points Today</p>
64 </div>
65 <div class="align-self-center">
66 <i class="fas fa-database fa-2x"></i>
67 </div>
68 </div>
69 </div>
70 </div>
71 </div>
72 </div>
73
74 <!-- Main Charts Row -->
75 <div class="row mb-4">
76 <div class="col-lg-8">
77 <div class="card">
78 <div class="card-header d-flex justify-content-between align-items-center">
79 <h5 class="mb-0">Real-time Sensor Data</h5>
80 <div class="btn-group" role="group">
81 <button type="button" class="btn btn-outline-primary btn-sm" onclick="changeTimeRange('1h')">1H</button>
82 <button type="button" class="btn btn-outline-primary btn-sm active" onclick="changeTimeRange('24h')">24H</button>
83 <button type="button" class="btn btn-outline-primary btn-sm" onclick="changeTimeRange('7d')">7D</button>
84 </div>
85 </div>
86 <div class="card-body">
87 <canvas id="mainChart" height="300"></canvas>
88 </div>
89 </div>
90 </div>
91
92 <div class="col-lg-4">
93 <div class="card h-100">
94 <div class="card-header">
95 <h5 class="mb-0">Current Status</h5>
96 </div>
97 <div class="card-body">
98 <div id="gauge-container">
99 <canvas id="temperatureGauge" width="200" height="150"></canvas>
100 <canvas id="humidityGauge" width="200" height="150"></canvas>
101 </div>
102 </div>
103 </div>
104 </div>
105 </div>
106
107 <!-- Analytics and Alerts Row -->
108 <div class="row mb-4">
109 <div class="col-lg-6">
110 <div class="card">
111 <div class="card-header">
112 <h5 class="mb-0">Predictive Analytics</h5>
113 </div>
114 <div class="card-body">
115 <div id="predictions-container">
116 <div class="prediction-item mb-3">
117 <h6>Temperature Forecast</h6>
118 <div class="d-flex align-items-center">
119 <span class="predicted-value" id="temp-prediction">--°C</span>
120 <small class="text-muted ms-2" id="temp-prediction-time">in 6 hours</small>
121 </div>
122 <div class="progress mt-2" style="height: 5px;">
123 <div class="progress-bar" id="temp-confidence" style="width: 0%"></div>
124 </div>
125 </div>
126
127 <div class="prediction-item mb-3">
128 <h6>Energy Consumption</h6>
129 <div class="d-flex align-items-center">
130 <span class="predicted-value" id="energy-prediction">-- kWh</span>
131 <small class="text-muted ms-2">today's total</small>
132 </div>
133 </div>
134 </div>
135 </div>
136 </div>
137 </div>
138
139 <div class="col-lg-6">
140 <div class="card">
141 <div class="card-header d-flex justify-content-between align-items-center">
142 <h5 class="mb-0">Smart Alerts</h5>
143 <button class="btn btn-outline-primary btn-sm" onclick="refreshAlerts()">
144 <i class="fas fa-sync-alt"></i>
145 </button>
146 </div>
147 <div class="card-body">
148 <div id="alerts-container" style="max-height: 300px; overflow-y: auto;">
149 <!-- Alerts will be populated here -->
150 </div>
151 </div>
152 </div>
153 </div>
154 </div>
155
156 <!-- Automation Controls -->
157 <div class="row">
158 <div class="col-12">
159 <div class="card">
160 <div class="card-header">
161 <h5 class="mb-0">Automation & Control</h5>
162 </div>
163 <div class="card-body">
164 <div class="row">
165 <div class="col-lg-6">
166 <h6>Quick Actions</h6>
167 <div class="btn-group-vertical w-100" role="group">
168 <button type="button" class="btn btn-outline-success mb-2" onclick="executeAutomation('optimize_climate')">
169 <i class="fas fa-thermometer-half"></i> Optimize Climate
170 </button>
171 <button type="button" class="btn btn-outline-info mb-2" onclick="executeAutomation('energy_save_mode')">
172 <i class="fas fa-leaf"></i> Energy Save Mode
173 </button>
174 <button type="button" class="btn btn-outline-warning mb-2" onclick="generateReport()">
175 <i class="fas fa-file-pdf"></i> Generate Report
176 </button>
177 </div>
178 </div>
179
180 <div class="col-lg-6">
181 <h6>Automation Rules</h6>
182 <div id="automation-rules">
183 <!-- Rules will be populated here -->
184 </div>
185 </div>
186 </div>
187 </div>
188 </div>
189 </div>
190 </div>
191</div>
192{% endblock %}
193
194{% block scripts %}
195<script>
196class AdvancedIoTDashboard {
197 constructor() {
198 this.charts = {};
199 this.currentTimeRange = '24h';
200 this.init();
201 }
202
203 init() {
204 this.initCharts();
205 this.startRealTimeUpdates();
206 this.loadInitialData();
207 }
208
209 initCharts() {
210 // Main multi-sensor chart
211 const mainCtx = document.getElementById('mainChart').getContext('2d');
212 this.charts.main = new Chart(mainCtx, {
213 type: 'line',
214 data: {
215 labels: [],
216 datasets: [
217 {
218 label: 'Temperature (°C)',
219 data: [],
220 borderColor: 'rgb(255, 99, 132)',
221 backgroundColor: 'rgba(255, 99, 132, 0.1)',
222 yAxisID: 'y',
223 tension: 0.4
224 },
225 {
226 label: 'Humidity (%)',
227 data: [],
228 borderColor: 'rgb(54, 162, 235)',
229 backgroundColor: 'rgba(54, 162, 235, 0.1)',
230 yAxisID: 'y1',
231 tension: 0.4
232 }
233 ]
234 },
235 options: {
236 responsive: true,
237 maintainAspectRatio: false,
238 interaction: {
239 mode: 'index',
240 intersect: false,
241 },
242 scales: {
243 x: {
244 display: true,
245 title: {
246 display: true,
247 text: 'Time'
248 }
249 },
250 y: {
251 type: 'linear',
252 display: true,
253 position: 'left',
254 title: {
255 display: true,
256 text: 'Temperature (°C)'
257 }
258 },
259 y1: {
260 type: 'linear',
261 display: true,
262 position: 'right',
263 title: {
264 display: true,
265 text: 'Humidity (%)'
266 },
267 grid: {
268 drawOnChartArea: false,
269 },
270 }
271 },
272 plugins: {
273 legend: {
274 position: 'top',
275 },
276 tooltip: {
277 mode: 'index',
278 intersect: false,
279 }
280 }
281 }
282 });
283
284 // Gauge charts for current values
285 this.initGauges();
286 }
287
288 initGauges() {
289 // Temperature gauge
290 const tempCtx = document.getElementById('temperatureGauge').getContext('2d');
291 this.charts.tempGauge = new Chart(tempCtx, {
292 type: 'doughnut',
293 data: {
294 labels: ['Current', 'Remaining'],
295 datasets: [{
296 data: [22, 78], // Start with 22°C out of 100
297 backgroundColor: ['#ff6384', '#e0e0e0'],
298 borderWidth: 0
299 }]
300 },
301 options: {
302 cutout: '70%',
303 plugins: {
304 legend: {
305 display: false
306 }
307 }
308 }
309 });
310
311 // Humidity gauge
312 const humCtx = document.getElementById('humidityGauge').getContext('2d');
313 this.charts.humGauge = new Chart(humCtx, {
314 type: 'doughnut',
315 data: {
316 labels: ['Current', 'Remaining'],
317 datasets: [{
318 data: [50, 50], // Start with 50% humidity
319 backgroundColor: ['#36a2eb', '#e0e0e0'],
320 borderWidth: 0
321 }]
322 },
323 options: {
324 cutout: '70%',
325 plugins: {
326 legend: {
327 display: false
328 }
329 }
330 }
331 });
332 }
333
334 async loadInitialData() {
335 try {
336 // Load historical data for charts
337 const response = await fetch(`/api/historical-data?range=${this.currentTimeRange}`);
338 const data = await response.json();
339
340 this.updateMainChart(data);
341 this.updateKPIs(data);
342
343 } catch (error) {
344 console.error('Error loading initial data:', error);
345 }
346 }
347
348 startRealTimeUpdates() {
349 // Update every 10 seconds
350 setInterval(async () => {
351 await this.updateRealTimeData();
352 }, 10000);
353
354 // Initial update
355 this.updateRealTimeData();
356 }
357
358 async updateRealTimeData() {
359 try {
360 const response = await fetch('/api/real-time-data');
361 const data = await response.json();
362
363 this.updateCurrentValues(data);
364 this.updateGauges(data);
365 this.updateAlerts(data.alerts || []);
366
367 } catch (error) {
368 console.error('Error updating real-time data:', error);
369 }
370 }
371
372 updateMainChart(data) {
373 if (!data.historical) return;
374
375 const chart = this.charts.main;
376 chart.data.labels = data.historical.timestamps;
377 chart.data.datasets[0].data = data.historical.temperature;
378 chart.data.datasets[1].data = data.historical.humidity;
379 chart.update('none');
380 }
381
382 updateCurrentValues(data) {
383 // Update KPI cards
384 document.getElementById('total-sensors').textContent = data.total_sensors || '--';
385 document.getElementById('system-health').textContent = data.system_health || '--';
386 document.getElementById('active-alerts').textContent = data.active_alerts || '0';
387 document.getElementById('data-points').textContent = data.data_points_today || '--';
388 }
389
390 updateGauges(data) {
391 if (data.current_temperature !== undefined) {
392 const tempChart = this.charts.tempGauge;
393 const tempValue = Math.min(100, Math.max(0, data.current_temperature * 2)); // Scale 0-50°C to 0-100
394 tempChart.data.datasets[0].data = [tempValue, 100 - tempValue];
395 tempChart.update('none');
396 }
397
398 if (data.current_humidity !== undefined) {
399 const humChart = this.charts.humGauge;
400 humChart.data.datasets[0].data = [data.current_humidity, 100 - data.current_humidity];
401 humChart.update('none');
402 }
403 }
404
405 updateAlerts(alerts) {
406 const container = document.getElementById('alerts-container');
407
408 if (alerts.length === 0) {
409 container.innerHTML = '<p class="text-muted">No active alerts</p>';
410 return;
411 }
412
413 const alertsHTML = alerts.map(alert => `
414 <div class="alert alert-${this.getAlertClass(alert.severity)} alert-dismissible fade show" role="alert">
415 <strong>${alert.type}:</strong> ${alert.message}
416 <small class="d-block text-muted">${alert.timestamp}</small>
417 ${alert.action ? `
418 <button type="button" class="btn btn-sm btn-outline-${this.getAlertClass(alert.severity)} mt-2"
419 onclick="executeRecommendedAction('${alert.action}')">
420 Execute Action
421 </button>
422 ` : ''}
423 </div>
424 `).join('');
425
426 container.innerHTML = alertsHTML;
427 }
428
429 getAlertClass(severity) {
430 const severityMap = {
431 'high': 'danger',
432 'medium': 'warning',
433 'low': 'info'
434 };
435 return severityMap[severity] || 'info';
436 }
437}
438
439// Global functions for UI interactions
440function changeTimeRange(range) {
441 dashboard.currentTimeRange = range;
442 dashboard.loadInitialData();
443
444 // Update active button
445 document.querySelectorAll('.btn-group .btn').forEach(btn => btn.classList.remove('active'));
446 event.target.classList.add('active');
447}
448
449async function executeAutomation(action) {
450 try {
451 const response = await fetch('/api/automation-trigger', {
452 method: 'POST',
453 headers: {
454 'Content-Type': 'application/json',
455 },
456 body: JSON.stringify({ action: action })
457 });
458
459 const result = await response.json();
460
461 if (result.success) {
462 showNotification(`Automation "${action}" executed successfully`, 'success');
463 } else {
464 showNotification(`Failed to execute "${action}"`, 'error');
465 }
466 } catch (error) {
467 showNotification('Connection error', 'error');
468 }
469}
470
471async function executeRecommendedAction(action) {
472 await executeAutomation(action);
473}
474
475function generateReport() {
476 // Trigger report generation
477 window.open('/api/generate-report', '_blank');
478}
479
480function refreshAlerts() {
481 dashboard.updateRealTimeData();
482}
483
484function showNotification(message, type) {
485 // Create toast notification
486 const toast = document.createElement('div');
487 toast.className = `alert alert-${type === 'error' ? 'danger' : type} alert-dismissible fade show position-fixed`;
488 toast.style.cssText = 'top: 20px; right: 20px; z-index: 1050; min-width: 300px;';
489
490 toast.innerHTML = `
491 ${message}
492 <button type="button" class="btn-close" data-bs-dismiss="alert"></button>
493 `;
494
495 document.body.appendChild(toast);
496
497 setTimeout(() => {
498 if (toast.parentNode) {
499 toast.remove();
500 }
501 }, 5000);
502}
503
504// Initialize dashboard when page loads
505document.addEventListener('DOMContentLoaded', () => {
506 window.dashboard = new AdvancedIoTDashboard();
507});
508</script>
509{% endblock %}
🎯 ADVANCED DEMO DAY (75-85 мин): Showcase продвинутых IoT решений
🆕 Формат: “IoT Data Science Conference 2025”
- Время на команду: 3 минуты
- Live demo: Обязательно показать analytics в действии
- Technical Q&A: 1 минута от экспертов
🆕 Структура презентации:
- Business Problem: Какую реальную задачу решает аналитика (45 сек)
- Data Science Demo: Показ предсказаний, аномалий, автоматизации (90 сек)
- ROI Demonstration: Конкретные цифры экономии/эффективности (45 сек)
🆕 Экспертная комиссия:
- Chief Data Officer - оценивает качество аналитики
- IoT Solution Architect - техническая экспертиза
- Business Stakeholder - практическая ценность
- End User Representative - удобство использования
🔍 DATA INSIGHTS CLINIC (85-87 мин): Анализ полученных результатов
Обсуждение инсайтов:
1📊 INSIGHTS ОТ КОМАНД:
2
3🔵 CLIMATE INSIGHTS:
4"Обнаружили, что комфортность падает каждый день в 14:00 -
5нужно автоматически включать кондиционер в 13:45"
6
7🔴 ENERGY INSIGHTS:
8"Ночное потребление составляет 40% от дневного -
9можно сэкономить $50/месяц правильной настройкой"
10
11🟢 AGRICULTURAL INSIGHTS:
12"Растения нуждаются в поливе не по расписанию,
13а когда влажность почвы падает ниже 30%"
14
15🟡 FACILITY INSIGHTS:
16"Паттерны движения показывают неэффективное использование пространства -
17можно перепланировать офис"
🔄 SPRINT RETRO (87-90 мин): От данных к решениям
🆕 Рефлексивные вопросы:
- Какой инсайт из данных вас больше всего удивил?
- Что сложнее - собрать данные или извлечь из них пользу?
- 🆕 Как автоматизация меняет роль человека в IoT системах?
- 🆕 Какие этические вопросы возникают при анализе IoT данных?
- Готовы ли вы доверить важные решения алгоритмам?
📝 Sprint Backlog (🆕 Capstone проект)
🆕 Основное задание: “IoT Data Scientist: Создаю предиктивную систему для школы”
Сценарий: Директор вашей школы хочет внедрить IoT систему для оптимизации работы здания. Вас назначили главным аналитиком проекта. Нужно создать систему, которая будет предсказывать и автоматически решать проблемы.
🏫 Техническое задание:
1ОБЪЕКТ: Ваша школа (3-4 этажа, 30+ кабинетов, 500+ учеников)
2ЦЕЛЬ: Снижение расходов на 20% + повышение комфорта
3БЮДЖЕТ: $5,000 на IoT + аналитическую систему
4
5ОСНОВНЫЕ НАПРАВЛЕНИЯ:
6
7🌡️ КЛИМАТ-КОНТРОЛЬ:
8├── Мониторинг температуры в каждом кабинете
9├── Автоматическая регулировка отопления/кондиционирования
10├── Предсказание пиковых нагрузок
11└── Оптимизация расписания работы систем
12
13💡 ЭНЕРГОМЕНЕДЖМЕНТ:
14├── Умное освещение с датчиками присутствия
15├── Автоматическое отключение оборудования
16├── Анализ энергопотребления по часам/дням
17└── Поиск точек избыточного потребления
18
19🚪 УПРАВЛЕНИЕ ПРОСТРАНСТВОМ:
20├── Мониторинг загруженности кабинетов
21├── Оптимизация расписания использования помещений
22├── Автоматическая система бронирования
23└── Анализ паттернов перемещения людей
24
25🛡️ БЕЗОПАСНОСТЬ И МОНИТОРИНГ:
26├── Система контроля доступа
27├── Мониторинг качества воздуха (CO2, пыль)
28├── Детекция аномальных ситуаций
29└── Автоматические уведомления службам
30
31📊 АНАЛИТИКА И ОТЧЕТНОСТЬ:
32├── Дашборды для администрации
33├── Еженедельные отчеты по эффективности
34├── Предиктивная аналитика потребления ресурсов
35└── Рекомендации по оптимизации
🆕 Deliverables (что нужно сдать):
- Техническая архитектура - схема размещения датчиков и системы
- Веб-дашборд - рабочий интерфейс с real-time данными
- Predictive models - алгоритмы предсказания и оптимизации
- Business case - ROI analysis и план внедрения
- Pilot deployment - реальное тестирование в одном кабинете
- 🆕 Presentation для директора - 10-минутная презентация с demo
🆕 Этапы реализации (4 недели):
Неделя 1: Research & Data Collection
- Audit существующих систем школы
- Установка pilot датчиков в одном кабинете
- Сбор baseline данных
- Интервью с администрацией и учителями
Неделя 2: Analytics Development
- Разработка алгоритмов анализа
- Создание predictive models
- Тестирование на исторических данных
- Настройка automation rules
Неделя 3: Dashboard Development
- Создание admin dashboard
- Mobile-friendly интерфейсы
- Integration всех data sources
- User testing с администрацией
Неделя 4: Pilot & Presentation
- Полномасштабное тестирование
- Сбор feedback и итерация
- Подготовка business presentation
- Final demo для стейкхолдеров
📊 Sprint Metrics & 🎒 Resources
🚀 Sprint успешен, если:
- ✅ Каждая команда создала working analytics dashboard с предсказаниями
- ✅ Реализованы автоматические действия на основе данных датчиков
- ✅ Учащиеся понимают data pipeline от сбора до действий
- ✅ Могут объяснить business value аналитики IoT данных
- ✅ 🆕 Демонстрируют data-driven мышление при решении проблем
🛠️ Необходимое оборудование:
- Raspberry Pi с расширенным набором датчиков
- Дополнительные storage для historical data
- Более мощные Pi или cloud resources для ML
- Professional демо-датчики для реалистичных данных
🔗 Связь с Sprint #23: Следующий спринт - REST API для IoT устройств. Созданные аналитические системы станут основой для API endpoints, которые другие системы смогут использовать для получения инсайтов и управления автоматизацией.