Skip to main content

📊 Веб-интерфейсы для датчиков. Превращаем данные 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 через впечатляющий кейс

🆕 Демо-эксперимент “Предсказание будущего”:

  1. Учитель показывает готовый дашборд с историческими данными температуры за неделю
  2. Демонстрирует trend analysis - “Смотрите, каждый день в 14:00 температура поднимается”
  3. Показывает prediction - “Завтра в 14:00 будет 26°C, включим кондиционер заранее”
  4. 🆕 Триггерит автоматическое действие - дашборд отправляет команду включить вентилятор
  5. Показывает alert system - “Влажность критично низкая, отправляю SMS родителям”
  6. 🆕 Экспортирует 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 минута от экспертов

🆕 Структура презентации:

  1. Business Problem: Какую реальную задачу решает аналитика (45 сек)
  2. Data Science Demo: Показ предсказаний, аномалий, автоматизации (90 сек)
  3. 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 мин): От данных к решениям

🆕 Рефлексивные вопросы:

  1. Какой инсайт из данных вас больше всего удивил?
  2. Что сложнее - собрать данные или извлечь из них пользу?
  3. 🆕 Как автоматизация меняет роль человека в IoT системах?
  4. 🆕 Какие этические вопросы возникают при анализе IoT данных?
  5. Готовы ли вы доверить важные решения алгоритмам?

📝 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 (что нужно сдать):

  1. Техническая архитектура - схема размещения датчиков и системы
  2. Веб-дашборд - рабочий интерфейс с real-time данными
  3. Predictive models - алгоритмы предсказания и оптимизации
  4. Business case - ROI analysis и план внедрения
  5. Pilot deployment - реальное тестирование в одном кабинете
  6. 🆕 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, которые другие системы смогут использовать для получения инсайтов и управления автоматизацией.