""" Sensor service business logic """ import logging from datetime import datetime, timedelta from typing import List, Dict, Any, Optional import json logger = logging.getLogger(__name__) class SensorService: """Service for managing sensors and sensor data""" def __init__(self, db, redis_client): self.db = db self.redis = redis_client async def get_sensors(self, room: Optional[str] = None, sensor_type: Optional[str] = None, status: Optional[str] = None) -> List[Dict[str, Any]]: """Get sensors with optional filtering""" try: query = {} if room: query["room"] = room if sensor_type: query["sensor_type"] = sensor_type if status: query["status"] = status cursor = self.db.sensors.find(query) sensors = [] async for sensor in cursor: sensor["_id"] = str(sensor["_id"]) sensors.append(sensor) return sensors except Exception as e: logger.error(f"Error getting sensors: {e}") raise async def get_sensor_details(self, sensor_id: str) -> Optional[Dict[str, Any]]: """Get detailed sensor information""" try: sensor = await self.db.sensors.find_one({"sensor_id": sensor_id}) if sensor: sensor["_id"] = str(sensor["_id"]) # Get recent readings recent_readings = await self.get_sensor_data(sensor_id, limit=10) sensor["recent_readings"] = recent_readings.get("readings", []) return sensor return None except Exception as e: logger.error(f"Error getting sensor details: {e}") raise async def get_sensor_data(self, sensor_id: str, start_time: Optional[int] = None, end_time: Optional[int] = None, limit: int = 100, offset: int = 0) -> Dict[str, Any]: """Get historical sensor data""" try: query = {"sensor_id": sensor_id} if start_time or end_time: query["timestamp"] = {} if start_time: query["timestamp"]["$gte"] = start_time if end_time: query["timestamp"]["$lte"] = end_time # Get total count total_count = await self.db.sensor_readings.count_documents(query) # Get readings cursor = self.db.sensor_readings.find(query).sort("timestamp", -1).skip(offset).limit(limit) readings = [] async for reading in cursor: reading["_id"] = str(reading["_id"]) readings.append(reading) return { "readings": readings, "total_count": total_count, "execution_time_ms": 0 # Placeholder } except Exception as e: logger.error(f"Error getting sensor data: {e}") raise async def create_sensor(self, sensor_data) -> Dict[str, Any]: """Create a new sensor""" try: # Check if sensor already exists existing = await self.db.sensors.find_one({"sensor_id": sensor_data.sensor_id}) if existing: raise ValueError(f"Sensor {sensor_data.sensor_id} already exists") # Create sensor document sensor_doc = { "sensor_id": sensor_data.sensor_id, "name": sensor_data.name, "sensor_type": sensor_data.sensor_type.value if hasattr(sensor_data.sensor_type, 'value') else str(sensor_data.sensor_type), "room": sensor_data.room, "location": sensor_data.location if hasattr(sensor_data, 'location') else None, "status": "active", "created_at": datetime.utcnow(), "updated_at": datetime.utcnow() } result = await self.db.sensors.insert_one(sensor_doc) return {"created_at": datetime.utcnow()} except Exception as e: logger.error(f"Error creating sensor: {e}") raise async def update_sensor(self, sensor_id: str, update_data: Dict[str, Any]) -> bool: """Update sensor metadata""" try: update_data["updated_at"] = datetime.utcnow() result = await self.db.sensors.update_one( {"sensor_id": sensor_id}, {"$set": update_data} ) return result.modified_count > 0 except Exception as e: logger.error(f"Error updating sensor: {e}") raise async def delete_sensor(self, sensor_id: str) -> Dict[str, Any]: """Delete a sensor and its data""" try: # Delete readings readings_result = await self.db.sensor_readings.delete_many({"sensor_id": sensor_id}) # Delete sensor await self.db.sensors.delete_one({"sensor_id": sensor_id}) return {"readings_deleted": readings_result.deleted_count} except Exception as e: logger.error(f"Error deleting sensor: {e}") raise async def ingest_sensor_data(self, sensor_data) -> Dict[str, Any]: """Ingest real-time sensor data""" try: # Create reading document reading_doc = { "sensor_id": sensor_data.sensor_id, "timestamp": sensor_data.timestamp, "value": sensor_data.value, "unit": sensor_data.unit if hasattr(sensor_data, 'unit') else None, "room": sensor_data.room if hasattr(sensor_data, 'room') else None, "created_at": datetime.utcnow() } # Store in database await self.db.sensor_readings.insert_one(reading_doc) # Cache recent value in Redis if self.redis: cache_key = f"sensor:{sensor_data.sensor_id}:latest" await self.redis.setex(cache_key, 3600, json.dumps(reading_doc, default=str)) return {"success": True} except Exception as e: logger.error(f"Error ingesting sensor data: {e}") raise async def export_data(self, start_time: int, end_time: int, sensor_ids: Optional[str] = None, format: str = "json") -> Dict[str, Any]: """Export sensor data""" try: query = { "timestamp": {"$gte": start_time, "$lte": end_time} } if sensor_ids: sensor_list = [s.strip() for s in sensor_ids.split(",")] query["sensor_id"] = {"$in": sensor_list} cursor = self.db.sensor_readings.find(query).sort("timestamp", 1) readings = [] async for reading in cursor: reading["_id"] = str(reading["_id"]) readings.append(reading) return { "format": format, "data": readings, "total_records": len(readings), "period": {"start": start_time, "end": end_time} } except Exception as e: logger.error(f"Error exporting data: {e}") raise async def get_events(self, severity: Optional[str] = None, event_type: Optional[str] = None, hours: int = 24, limit: int = 50) -> List[Dict[str, Any]]: """Get system events""" try: start_time = datetime.utcnow() - timedelta(hours=hours) query = {"timestamp": {"$gte": start_time}} if severity: query["severity"] = severity if event_type: query["event_type"] = event_type cursor = self.db.system_events.find(query).sort("timestamp", -1).limit(limit) events = [] async for event in cursor: event["_id"] = str(event["_id"]) events.append(event) return events except Exception as e: logger.error(f"Error getting events: {e}") return [] async def cleanup_old_data(self, cutoff_date: datetime): """Clean up old sensor data""" try: result = await self.db.sensor_readings.delete_many({ "created_at": {"$lt": cutoff_date} }) logger.info(f"Cleaned up {result.deleted_count} old sensor readings") except Exception as e: logger.error(f"Error cleaning up old data: {e}") raise