Create secure, scalable IoT device management systems with Laravel, handling sensor data, real-time communication, and device lifecycle management for connected applications.
Table Of Contents
- Understanding IoT Architecture with Laravel
- Device Management and Authentication
- Telemetry Data Management
- Device Command and Control
- Real-time Communication
- Alert and Monitoring System
Understanding IoT Architecture with Laravel
Internet of Things (IoT) applications require robust backend systems that can handle massive volumes of sensor data, manage device states, and provide real-time communication channels. Laravel's event-driven architecture, combined with its powerful queue system and broadcasting capabilities, makes it an excellent choice for IoT backends.
Building IoT systems involves managing device authentication, handling time-series data efficiently, implementing command-and-control systems, and ensuring reliable communication protocols. This becomes crucial when developing scalable Laravel applications that need to process thousands of concurrent device connections.
Device Management and Authentication
IoT Device Model and Authentication
Create comprehensive device management with secure authentication:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Support\Str;
class IoTDevice extends Model
{
protected $table = 'iot_devices';
protected $fillable = [
'device_id',
'name',
'type',
'model',
'firmware_version',
'user_id',
'location_data',
'configuration',
'status',
'last_seen_at',
'api_key_hash',
'certificate_thumbprint',
];
protected $casts = [
'location_data' => 'array',
'configuration' => 'array',
'last_seen_at' => 'datetime',
'is_active' => 'boolean',
];
protected $hidden = [
'api_key_hash',
'certificate_thumbprint',
];
const STATUS_ONLINE = 'online';
const STATUS_OFFLINE = 'offline';
const STATUS_MAINTENANCE = 'maintenance';
const STATUS_ERROR = 'error';
public static function boot()
{
parent::boot();
static::creating(function ($device) {
if (empty($device->device_id)) {
$device->device_id = 'DEV_' . Str::upper(Str::random(12));
}
if (empty($device->api_key_hash)) {
$apiKey = Str::random(64);
$device->api_key_hash = hash('sha256', $apiKey);
// Store unhashed key temporarily for initial setup
$device->setAttribute('temp_api_key', $apiKey);
}
});
}
public function owner(): BelongsTo
{
return $this->belongsTo(User::class, 'user_id');
}
public function telemetryData(): HasMany
{
return $this->hasMany(DeviceTelemetry::class, 'device_id', 'device_id');
}
public function commands(): HasMany
{
return $this->hasMany(DeviceCommand::class, 'device_id', 'device_id');
}
public function alerts(): HasMany
{
return $this->hasMany(DeviceAlert::class, 'device_id', 'device_id');
}
public function maintenanceLogs(): HasMany
{
return $this->hasMany(DeviceMaintenanceLog::class, 'device_id', 'device_id');
}
public function scopeOnline($query)
{
return $query->where('status', self::STATUS_ONLINE)
->where('last_seen_at', '>=', now()->subMinutes(5));
}
public function scopeByType($query, string $type)
{
return $query->where('type', $type);
}
public function scopeByLocation($query, float $lat, float $lng, float $radius = 10)
{
return $query->whereRaw(
'ST_Distance_Sphere(
POINT(JSON_EXTRACT(location_data, "$.longitude"), JSON_EXTRACT(location_data, "$.latitude")),
POINT(?, ?)
) <= ?',
[$lng, $lat, $radius * 1000] // Convert km to meters
);
}
public function updateLastSeen(): void
{
$this->update([
'last_seen_at' => now(),
'status' => self::STATUS_ONLINE,
]);
}
public function isOnline(): bool
{
return $this->status === self::STATUS_ONLINE &&
$this->last_seen_at &&
$this->last_seen_at->diffInMinutes(now()) <= 5;
}
public function generateNewApiKey(): string
{
$apiKey = Str::random(64);
$this->update(['api_key_hash' => hash('sha256', $apiKey)]);
return $apiKey;
}
public function verifyApiKey(string $apiKey): bool
{
return hash_equals($this->api_key_hash, hash('sha256', $apiKey));
}
public function getCapabilities(): array
{
$capabilities = $this->configuration['capabilities'] ?? [];
return array_filter([
'telemetry' => $capabilities['telemetry'] ?? true,
'commands' => $capabilities['commands'] ?? false,
'firmware_update' => $capabilities['firmware_update'] ?? false,
'configuration_sync' => $capabilities['configuration_sync'] ?? false,
]);
}
}
Device Authentication Middleware
Implement secure device authentication:
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use App\Models\IoTDevice;
use Symfony\Component\HttpFoundation\Response;
class AuthenticateIoTDevice
{
public function handle(Request $request, Closure $next): Response
{
$deviceId = $request->header('X-Device-ID');
$apiKey = $request->header('X-API-Key');
$signature = $request->header('X-Signature');
if (!$deviceId || !$apiKey) {
return response()->json([
'error' => 'Missing device credentials',
'code' => 'DEVICE_AUTH_REQUIRED'
], 401);
}
$device = IoTDevice::where('device_id', $deviceId)->first();
if (!$device) {
return response()->json([
'error' => 'Device not found',
'code' => 'DEVICE_NOT_FOUND'
], 404);
}
if (!$device->verifyApiKey($apiKey)) {
return response()->json([
'error' => 'Invalid API key',
'code' => 'INVALID_API_KEY'
], 401);
}
// Verify signature for sensitive operations
if ($signature && !$this->verifySignature($request, $device, $signature)) {
return response()->json([
'error' => 'Invalid signature',
'code' => 'INVALID_SIGNATURE'
], 401);
}
// Update device last seen
$device->updateLastSeen();
// Add device to request for use in controllers
$request->attributes->set('iot_device', $device);
return $next($request);
}
protected function verifySignature(Request $request, IoTDevice $device, string $signature): bool
{
$payload = $request->getContent();
$timestamp = $request->header('X-Timestamp');
if (!$timestamp || abs(time() - $timestamp) > 300) { // 5 minutes tolerance
return false;
}
$expectedSignature = hash_hmac(
'sha256',
$timestamp . '.' . $payload,
$device->api_key_hash
);
return hash_equals($signature, $expectedSignature);
}
}
Telemetry Data Management
High-Performance Telemetry Storage
Handle massive volumes of sensor data efficiently:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class DeviceTelemetry extends Model
{
protected $table = 'device_telemetry';
protected $fillable = [
'device_id',
'sensor_type',
'measurement_type',
'value',
'unit',
'quality',
'metadata',
'recorded_at',
];
protected $casts = [
'value' => 'float',
'quality' => 'float',
'metadata' => 'array',
'recorded_at' => 'datetime',
];
public $timestamps = false; // Use recorded_at instead
public function device(): BelongsTo
{
return $this->belongsTo(IoTDevice::class, 'device_id', 'device_id');
}
public function scopeForPeriod($query, \DateTime $start, \DateTime $end)
{
return $query->whereBetween('recorded_at', [$start, $end]);
}
public function scopeBySensorType($query, string $sensorType)
{
return $query->where('sensor_type', $sensorType);
}
public function scopeByMeasurementType($query, string $measurementType)
{
return $query->where('measurement_type', $measurementType);
}
public function scopeWithGoodQuality($query, float $minQuality = 0.8)
{
return $query->where('quality', '>=', $minQuality);
}
}
// Service for efficient telemetry processing
namespace App\Services;
use App\Models\DeviceTelemetry;
use App\Models\IoTDevice;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Redis;
use Carbon\Carbon;
class TelemetryService
{
protected array $batchBuffer = [];
protected int $batchSize = 1000;
public function storeTelemetryData(string $deviceId, array $telemetryData): array
{
$device = IoTDevice::where('device_id', $deviceId)->first();
if (!$device) {
throw new \InvalidArgumentException("Device not found: {$deviceId}");
}
$processedCount = 0;
$errors = [];
foreach ($telemetryData as $reading) {
try {
$this->validateTelemetryReading($reading);
$this->addToBatch($deviceId, $reading);
$processedCount++;
} catch (\Exception $e) {
$errors[] = [
'reading' => $reading,
'error' => $e->getMessage(),
];
}
}
$this->flushBatch();
// Update device aggregates in background
\Illuminate\Support\Facades\Queue::push(
new \App\Jobs\UpdateDeviceAggregatesJob($deviceId)
);
return [
'processed' => $processedCount,
'errors' => $errors,
'device_status' => $device->status,
];
}
protected function validateTelemetryReading(array $reading): void
{
$required = ['sensor_type', 'measurement_type', 'value', 'recorded_at'];
foreach ($required as $field) {
if (!isset($reading[$field])) {
throw new \InvalidArgumentException("Missing required field: {$field}");
}
}
if (!is_numeric($reading['value'])) {
throw new \InvalidArgumentException("Value must be numeric");
}
// Validate timestamp
try {
Carbon::parse($reading['recorded_at']);
} catch (\Exception $e) {
throw new \InvalidArgumentException("Invalid timestamp format");
}
}
protected function addToBatch(string $deviceId, array $reading): void
{
$this->batchBuffer[] = [
'device_id' => $deviceId,
'sensor_type' => $reading['sensor_type'],
'measurement_type' => $reading['measurement_type'],
'value' => (float) $reading['value'],
'unit' => $reading['unit'] ?? null,
'quality' => $reading['quality'] ?? 1.0,
'metadata' => $reading['metadata'] ?? null,
'recorded_at' => Carbon::parse($reading['recorded_at']),
];
if (count($this->batchBuffer) >= $this->batchSize) {
$this->flushBatch();
}
}
protected function flushBatch(): void
{
if (empty($this->batchBuffer)) {
return;
}
DB::table('device_telemetry')->insert($this->batchBuffer);
// Cache recent readings for real-time access
$this->cacheRecentReadings($this->batchBuffer);
$this->batchBuffer = [];
}
protected function cacheRecentReadings(array $readings): void
{
foreach ($readings as $reading) {
$key = "telemetry:{$reading['device_id']}:{$reading['sensor_type']}:latest";
Redis::setex($key, 3600, json_encode([
'value' => $reading['value'],
'unit' => $reading['unit'],
'recorded_at' => $reading['recorded_at']->toISOString(),
]));
}
}
public function getLatestReadings(string $deviceId, ?string $sensorType = null): array
{
$pattern = $sensorType
? "telemetry:{$deviceId}:{$sensorType}:latest"
: "telemetry:{$deviceId}:*:latest";
$keys = Redis::keys($pattern);
$readings = [];
foreach ($keys as $key) {
$data = Redis::get($key);
if ($data) {
$sensorTypeFromKey = explode(':', $key)[2];
$readings[$sensorTypeFromKey] = json_decode($data, true);
}
}
return $readings;
}
public function getAggregatedData(
string $deviceId,
string $sensorType,
string $measurementType,
\DateTime $start,
\DateTime $end,
string $interval = '1 hour'
): array {
$sql = "
SELECT
DATE_FORMAT(recorded_at, '%Y-%m-%d %H:00:00') as time_bucket,
AVG(value) as avg_value,
MIN(value) as min_value,
MAX(value) as max_value,
COUNT(*) as sample_count,
AVG(quality) as avg_quality
FROM device_telemetry
WHERE device_id = ?
AND sensor_type = ?
AND measurement_type = ?
AND recorded_at BETWEEN ? AND ?
AND quality >= 0.5
GROUP BY DATE_FORMAT(recorded_at, '%Y-%m-%d %H:00:00')
ORDER BY time_bucket
";
return collect(DB::select($sql, [
$deviceId,
$sensorType,
$measurementType,
$start->format('Y-m-d H:i:s'),
$end->format('Y-m-d H:i:s'),
]))->map(function ($row) {
return [
'timestamp' => Carbon::parse($row->time_bucket),
'average' => round($row->avg_value, 2),
'minimum' => round($row->min_value, 2),
'maximum' => round($row->max_value, 2),
'samples' => $row->sample_count,
'quality' => round($row->avg_quality, 2),
];
})->toArray();
}
}
Device Command and Control
Command Management System
Implement secure device command and control:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class DeviceCommand extends Model
{
protected $fillable = [
'device_id',
'command_type',
'command',
'parameters',
'priority',
'scheduled_at',
'expires_at',
'status',
'response',
'executed_at',
'retry_count',
];
protected $casts = [
'parameters' => 'array',
'response' => 'array',
'scheduled_at' => 'datetime',
'expires_at' => 'datetime',
'executed_at' => 'datetime',
];
const STATUS_PENDING = 'pending';
const STATUS_SENT = 'sent';
const STATUS_ACKNOWLEDGED = 'acknowledged';
const STATUS_EXECUTED = 'executed';
const STATUS_FAILED = 'failed';
const STATUS_EXPIRED = 'expired';
const PRIORITY_LOW = 1;
const PRIORITY_NORMAL = 5;
const PRIORITY_HIGH = 8;
const PRIORITY_CRITICAL = 10;
public function device(): BelongsTo
{
return $this->belongsTo(IoTDevice::class, 'device_id', 'device_id');
}
public function scopePending($query)
{
return $query->where('status', self::STATUS_PENDING)
->where(function ($q) {
$q->whereNull('expires_at')
->orWhere('expires_at', '>', now());
});
}
public function scopeByPriority($query, int $minPriority = self::PRIORITY_NORMAL)
{
return $query->where('priority', '>=', $minPriority);
}
public function isExpired(): bool
{
return $this->expires_at && $this->expires_at->isPast();
}
public function canRetry(): bool
{
return $this->retry_count < 3 && !$this->isExpired();
}
public function markAsSent(): void
{
$this->update(['status' => self::STATUS_SENT]);
}
public function markAsExecuted(array $response = null): void
{
$this->update([
'status' => self::STATUS_EXECUTED,
'response' => $response,
'executed_at' => now(),
]);
}
public function markAsFailed(string $reason): void
{
$this->update([
'status' => self::STATUS_FAILED,
'response' => ['error' => $reason],
'retry_count' => $this->retry_count + 1,
]);
}
}
// Command Service
namespace App\Services;
use App\Models\DeviceCommand;
use App\Models\IoTDevice;
use App\Events\DeviceCommandSent;
use App\Events\DeviceCommandExecuted;
use Illuminate\Support\Facades\Queue;
class DeviceCommandService
{
public function sendCommand(
string $deviceId,
string $commandType,
string $command,
array $parameters = [],
int $priority = DeviceCommand::PRIORITY_NORMAL,
?\DateTime $scheduledAt = null,
?\DateTime $expiresAt = null
): DeviceCommand {
$device = IoTDevice::where('device_id', $deviceId)->first();
if (!$device) {
throw new \InvalidArgumentException("Device not found: {$deviceId}");
}
if (!$device->isOnline() && $priority !== DeviceCommand::PRIORITY_CRITICAL) {
throw new \RuntimeException("Device is offline: {$deviceId}");
}
$this->validateCommand($commandType, $command, $parameters);
$deviceCommand = DeviceCommand::create([
'device_id' => $deviceId,
'command_type' => $commandType,
'command' => $command,
'parameters' => $parameters,
'priority' => $priority,
'scheduled_at' => $scheduledAt ?? now(),
'expires_at' => $expiresAt ?? now()->addHours(24),
'status' => DeviceCommand::STATUS_PENDING,
]);
// Queue command for immediate or scheduled delivery
if ($scheduledAt && $scheduledAt->isFuture()) {
Queue::later($scheduledAt, new \App\Jobs\SendDeviceCommandJob($deviceCommand));
} else {
Queue::push(new \App\Jobs\SendDeviceCommandJob($deviceCommand));
}
return $deviceCommand;
}
protected function validateCommand(string $commandType, string $command, array $parameters): void
{
$allowedCommands = [
'configuration' => ['update_config', 'reset_config', 'sync_config'],
'firmware' => ['update_firmware', 'rollback_firmware'],
'control' => ['restart', 'shutdown', 'set_mode'],
'diagnostic' => ['run_test', 'get_logs', 'health_check'],
];
if (!isset($allowedCommands[$commandType])) {
throw new \InvalidArgumentException("Invalid command type: {$commandType}");
}
if (!in_array($command, $allowedCommands[$commandType])) {
throw new \InvalidArgumentException("Invalid command: {$command} for type: {$commandType}");
}
// Validate parameters based on command
$this->validateCommandParameters($commandType, $command, $parameters);
}
protected function validateCommandParameters(string $commandType, string $command, array $parameters): void
{
switch ("{$commandType}.{$command}") {
case 'configuration.update_config':
if (empty($parameters['config_data'])) {
throw new \InvalidArgumentException("Missing config_data parameter");
}
break;
case 'firmware.update_firmware':
if (empty($parameters['firmware_url']) || empty($parameters['version'])) {
throw new \InvalidArgumentException("Missing firmware_url or version parameter");
}
break;
case 'control.set_mode':
if (empty($parameters['mode'])) {
throw new \InvalidArgumentException("Missing mode parameter");
}
break;
}
}
public function getPendingCommands(string $deviceId): array
{
return DeviceCommand::where('device_id', $deviceId)
->pending()
->orderBy('priority', 'desc')
->orderBy('scheduled_at')
->get()
->toArray();
}
public function acknowledgeCommand(int $commandId, array $response = []): void
{
$command = DeviceCommand::findOrFail($commandId);
$command->update([
'status' => DeviceCommand::STATUS_ACKNOWLEDGED,
'response' => array_merge($command->response ?? [], $response),
]);
event(new DeviceCommandSent($command));
}
public function executeCommand(int $commandId, array $response): void
{
$command = DeviceCommand::findOrFail($commandId);
$command->markAsExecuted($response);
event(new DeviceCommandExecuted($command));
}
}
Real-time Communication
WebSocket Integration for Real-time Updates
Implement real-time device communication:
<?php
namespace App\Http\Controllers\Api\IoT;
use App\Http\Controllers\Controller;
use App\Services\TelemetryService;
use App\Services\DeviceCommandService;
use App\Events\DeviceTelemetryReceived;
use App\Events\DeviceStatusChanged;
use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;
class DeviceCommunicationController extends Controller
{
protected TelemetryService $telemetryService;
protected DeviceCommandService $commandService;
public function __construct(
TelemetryService $telemetryService,
DeviceCommandService $commandService
) {
$this->middleware('auth.iot.device');
$this->telemetryService = $telemetryService;
$this->commandService = $commandService;
}
public function sendTelemetry(Request $request): JsonResponse
{
$device = $request->attributes->get('iot_device');
$request->validate([
'telemetry' => 'required|array',
'telemetry.*.sensor_type' => 'required|string',
'telemetry.*.measurement_type' => 'required|string',
'telemetry.*.value' => 'required|numeric',
'telemetry.*.recorded_at' => 'required|date',
'telemetry.*.unit' => 'nullable|string',
'telemetry.*.quality' => 'nullable|numeric|between:0,1',
'telemetry.*.metadata' => 'nullable|array',
]);
try {
$result = $this->telemetryService->storeTelemetryData(
$device->device_id,
$request->input('telemetry')
);
// Broadcast telemetry to subscribers
event(new DeviceTelemetryReceived($device, $request->input('telemetry')));
return response()->json([
'status' => 'success',
'processed' => $result['processed'],
'errors' => $result['errors'],
'next_poll' => now()->addMinutes(1)->toISOString(),
]);
} catch (\Exception $e) {
return response()->json([
'status' => 'error',
'message' => $e->getMessage(),
], 400);
}
}
public function pollCommands(Request $request): JsonResponse
{
$device = $request->attributes->get('iot_device');
$commands = $this->commandService->getPendingCommands($device->device_id);
return response()->json([
'commands' => $commands,
'count' => count($commands),
'poll_interval' => $this->calculatePollInterval($device),
]);
}
public function acknowledgeCommand(Request $request, int $commandId): JsonResponse
{
try {
$this->commandService->acknowledgeCommand(
$commandId,
$request->input('response', [])
);
return response()->json(['status' => 'acknowledged']);
} catch (\Exception $e) {
return response()->json([
'status' => 'error',
'message' => $e->getMessage(),
], 400);
}
}
public function reportCommandExecution(Request $request, int $commandId): JsonResponse
{
$request->validate([
'status' => 'required|in:success,failed',
'response' => 'required|array',
]);
try {
if ($request->input('status') === 'success') {
$this->commandService->executeCommand(
$commandId,
$request->input('response')
);
} else {
$command = \App\Models\DeviceCommand::findOrFail($commandId);
$command->markAsFailed($request->input('response.error', 'Unknown error'));
}
return response()->json(['status' => 'reported']);
} catch (\Exception $e) {
return response()->json([
'status' => 'error',
'message' => $e->getMessage(),
], 400);
}
}
public function updateStatus(Request $request): JsonResponse
{
$device = $request->attributes->get('iot_device');
$request->validate([
'status' => 'required|in:online,offline,maintenance,error',
'metadata' => 'nullable|array',
]);
$oldStatus = $device->status;
$device->update([
'status' => $request->input('status'),
'last_seen_at' => now(),
]);
if ($oldStatus !== $request->input('status')) {
event(new DeviceStatusChanged($device, $oldStatus, $request->input('status')));
}
return response()->json([
'status' => 'updated',
'device_status' => $device->status,
]);
}
protected function calculatePollInterval(\App\Models\IoTDevice $device): int
{
// Adjust poll interval based on device priority and activity
$baseInterval = 60; // seconds
if ($device->commands()->pending()->exists()) {
return min($baseInterval, 15); // Faster polling when commands are pending
}
if ($device->status === \App\Models\IoTDevice::STATUS_ERROR) {
return min($baseInterval, 30); // Faster polling for error devices
}
return $baseInterval;
}
}
// WebSocket Event Broadcasting
namespace App\Events;
use App\Models\IoTDevice;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class DeviceTelemetryReceived implements ShouldBroadcast
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public IoTDevice $device;
public array $telemetry;
public function __construct(IoTDevice $device, array $telemetry)
{
$this->device = $device;
$this->telemetry = $telemetry;
}
public function broadcastOn(): array
{
return [
new PrivateChannel('iot.device.' . $this->device->device_id),
new PrivateChannel('iot.user.' . $this->device->user_id),
];
}
public function broadcastAs(): string
{
return 'telemetry.received';
}
public function broadcastWith(): array
{
return [
'device_id' => $this->device->device_id,
'device_name' => $this->device->name,
'telemetry' => $this->telemetry,
'timestamp' => now()->toISOString(),
];
}
}
Alert and Monitoring System
Advanced Device Monitoring
Implement comprehensive device monitoring and alerting:
<?php
namespace App\Services;
use App\Models\IoTDevice;
use App\Models\DeviceAlert;
use App\Models\DeviceTelemetry;
use App\Notifications\DeviceAlertNotification;
use Illuminate\Support\Facades\Log;
class DeviceMonitoringService
{
protected array $alertRules = [];
public function __construct()
{
$this->loadAlertRules();
}
public function monitorDevice(IoTDevice $device): array
{
$alerts = [];
// Check device connectivity
if (!$device->isOnline()) {
$alerts[] = $this->createConnectivityAlert($device);
}
// Check telemetry thresholds
$telemetryAlerts = $this->checkTelemetryThresholds($device);
$alerts = array_merge($alerts, $telemetryAlerts);
// Check device health
$healthAlerts = $this->checkDeviceHealth($device);
$alerts = array_merge($alerts, $healthAlerts);
// Process and store alerts
foreach ($alerts as $alert) {
$this->processAlert($device, $alert);
}
return $alerts;
}
protected function createConnectivityAlert(IoTDevice $device): array
{
$minutesOffline = $device->last_seen_at
? $device->last_seen_at->diffInMinutes(now())
: 999;
return [
'type' => 'connectivity',
'severity' => $minutesOffline > 60 ? 'critical' : 'warning',
'message' => "Device offline for {$minutesOffline} minutes",
'metadata' => [
'last_seen' => $device->last_seen_at?->toISOString(),
'minutes_offline' => $minutesOffline,
],
];
}
protected function checkTelemetryThresholds(IoTDevice $device): array
{
$alerts = [];
$rules = $this->alertRules[$device->type] ?? [];
foreach ($rules as $rule) {
$latestReading = DeviceTelemetry::where('device_id', $device->device_id)
->where('sensor_type', $rule['sensor_type'])
->where('measurement_type', $rule['measurement_type'])
->orderBy('recorded_at', 'desc')
->first();
if (!$latestReading) {
continue;
}
$alert = $this->evaluateThresholdRule($rule, $latestReading);
if ($alert) {
$alerts[] = $alert;
}
}
return $alerts;
}
protected function evaluateThresholdRule(array $rule, DeviceTelemetry $reading): ?array
{
$value = $reading->value;
$thresholds = $rule['thresholds'];
if (isset($thresholds['critical'])) {
if ($this->checkThreshold($value, $thresholds['critical'])) {
return [
'type' => 'threshold',
'severity' => 'critical',
'message' => "Critical threshold exceeded for {$reading->sensor_type}",
'metadata' => [
'sensor_type' => $reading->sensor_type,
'measurement_type' => $reading->measurement_type,
'value' => $value,
'threshold' => $thresholds['critical'],
'unit' => $reading->unit,
],
];
}
}
if (isset($thresholds['warning'])) {
if ($this->checkThreshold($value, $thresholds['warning'])) {
return [
'type' => 'threshold',
'severity' => 'warning',
'message' => "Warning threshold exceeded for {$reading->sensor_type}",
'metadata' => [
'sensor_type' => $reading->sensor_type,
'measurement_type' => $reading->measurement_type,
'value' => $value,
'threshold' => $thresholds['warning'],
'unit' => $reading->unit,
],
];
}
}
return null;
}
protected function checkThreshold(float $value, array $threshold): bool
{
if (isset($threshold['min']) && $value < $threshold['min']) {
return true;
}
if (isset($threshold['max']) && $value > $threshold['max']) {
return true;
}
return false;
}
protected function checkDeviceHealth(IoTDevice $device): array
{
$alerts = [];
// Check for data quality issues
$lowQualityCount = DeviceTelemetry::where('device_id', $device->device_id)
->where('recorded_at', '>=', now()->subHour())
->where('quality', '<', 0.5)
->count();
if ($lowQualityCount > 10) {
$alerts[] = [
'type' => 'data_quality',
'severity' => 'warning',
'message' => "High number of low-quality readings: {$lowQualityCount}",
'metadata' => [
'low_quality_count' => $lowQualityCount,
'period' => '1 hour',
],
];
}
// Check firmware version
if ($this->isFirmwareOutdated($device)) {
$alerts[] = [
'type' => 'firmware',
'severity' => 'info',
'message' => "Firmware update available",
'metadata' => [
'current_version' => $device->firmware_version,
'latest_version' => $this->getLatestFirmwareVersion($device->type),
],
];
}
return $alerts;
}
protected function processAlert(IoTDevice $device, array $alertData): void
{
// Check if similar alert exists recently
$existingAlert = DeviceAlert::where('device_id', $device->device_id)
->where('type', $alertData['type'])
->where('created_at', '>=', now()->subMinutes(30))
->first();
if ($existingAlert) {
// Update existing alert
$existingAlert->update([
'count' => $existingAlert->count + 1,
'last_occurrence' => now(),
'metadata' => $alertData['metadata'],
]);
return;
}
// Create new alert
$alert = DeviceAlert::create([
'device_id' => $device->device_id,
'type' => $alertData['type'],
'severity' => $alertData['severity'],
'message' => $alertData['message'],
'metadata' => $alertData['metadata'],
'status' => 'active',
'count' => 1,
'last_occurrence' => now(),
]);
// Send notifications based on severity
if (in_array($alertData['severity'], ['critical', 'warning'])) {
$device->owner->notify(new DeviceAlertNotification($alert));
}
Log::info('Device alert created', [
'device_id' => $device->device_id,
'alert_type' => $alertData['type'],
'severity' => $alertData['severity'],
]);
}
protected function loadAlertRules(): void
{
$this->alertRules = [
'temperature_sensor' => [
[
'sensor_type' => 'temperature',
'measurement_type' => 'ambient',
'thresholds' => [
'critical' => ['min' => -20, 'max' => 50],
'warning' => ['min' => -10, 'max' => 40],
],
],
],
'humidity_sensor' => [
[
'sensor_type' => 'humidity',
'measurement_type' => 'relative',
'thresholds' => [
'critical' => ['min' => 10, 'max' => 90],
'warning' => ['min' => 20, 'max' => 80],
],
],
],
];
}
protected function isFirmwareOutdated(IoTDevice $device): bool
{
$latestVersion = $this->getLatestFirmwareVersion($device->type);
return version_compare($device->firmware_version, $latestVersion, '<');
}
protected function getLatestFirmwareVersion(string $deviceType): string
{
// In real implementation, this would check a firmware repository
return '1.2.3';
}
}
Building IoT applications with Laravel requires careful consideration of scalability, security, and real-time communication patterns. These advanced techniques enable creating robust IoT backends that can handle thousands of connected devices while providing comprehensive monitoring and control capabilities essential for modern web applications.
Add Comment
No comments yet. Be the first to comment!