📋 Phase Overview
This phase implements comprehensive alert and notification systems to proactively manage farm operations. You'll create weather-based alerts, task scheduling systems, equipment maintenance notifications, inventory alerts, and multi-channel communication (email, SMS, in-app notifications).
End Goal: Automated monitoring system that keeps you informed of critical events and upcoming tasks.
⚠️ Prerequisites
- Phase 5 Completed: Analytics and reporting functional
- Email Configuration: SMTP settings configured
- Weather Service: OpenWeather API integration working
- Task Scheduler: Laravel's task scheduling setup
Install packages for email, SMS, and push notifications.
Install Notification Services:
# Install Laravel Notifications (included by default)
# Install SMS service (Twilio)
composer require twilio/sdk
# Install push notification service
composer require laravel-notification-channels/webpush
# Install email queue processing
composer require laravel/horizon
Configure Notification Channels:
# Configure mail settings in .env
MAIL_MAILER=smtp
MAIL_HOST=your-smtp-host
MAIL_PORT=587
MAIL_USERNAME=your-email
MAIL_PASSWORD=your-password
# Add Twilio settings
TWILIO_SID=your-twilio-sid
TWILIO_TOKEN=your-twilio-token
TWILIO_FROM=your-twilio-number
Create comprehensive weather monitoring and alert system for frost warnings and extreme weather.
Weather Alert Service:
# Create weather alert service
php artisan make:command CheckWeatherAlerts
# Create notification classes
php artisan make:notification FrostWarning
php artisan make:notification ExtremeWeatherAlert
php artisan make:notification HarvestWeatherAlert
Weather Alert Implementation:
fetchCurrentWeather();
$forecast = $weatherService->get5DayForecast();
if (!$weather || !$forecast) {
$this->error('Failed to fetch weather data');
return 1;
}
$this->checkFrostWarnings($weather, $forecast);
$this->checkExtremeWeather($weather, $forecast);
$this->checkHarvestConditions($weather, $forecast);
return 0;
}
private function checkFrostWarnings($current, $forecast)
{
$frostTemp = config('weather.frost_warning_temp', 36);
$freezeTemp = config('weather.freeze_warning_temp', 32);
// Check current conditions
if ($current['main']['temp'] <= $freezeTemp) {
$this->sendAlert(new FrostWarning('freeze', $current['main']['temp'], 'current'));
} elseif ($current['main']['temp'] <= $frostTemp) {
$this->sendAlert(new FrostWarning('frost', $current['main']['temp'], 'current'));
}
// Check forecast for next 48 hours
foreach ($forecast['list'] as $item) {
$temp = $item['main']['temp_min'];
$datetime = \Carbon\Carbon::createFromTimestamp($item['dt']);
if ($datetime->diffInHours(now()) <= 48) {
if ($temp <= $freezeTemp) {
$this->sendAlert(new FrostWarning('freeze', $temp, $datetime));
} elseif ($temp <= $frostTemp) {
$this->sendAlert(new FrostWarning('frost', $temp, $datetime));
}
}
}
}
private function checkExtremeWeather($current, $forecast)
{
// Check for high winds
if (($current['wind']['speed'] ?? 0) > 25) { // 25+ mph
$this->sendAlert(new ExtremeWeatherAlert('high_wind', $current['wind']['speed']));
}
// Check for heavy rain
if (($current['rain']['1h'] ?? 0) > 0.5) { // >0.5 inches per hour
$this->sendAlert(new ExtremeWeatherAlert('heavy_rain', $current['rain']['1h']));
}
// Check for extreme temperatures
if ($current['main']['temp'] > 95) {
$this->sendAlert(new ExtremeWeatherAlert('extreme_heat', $current['main']['temp']));
}
}
private function sendAlert($notification)
{
$users = User::whereHas('preferences', function($query) {
$query->where('weather_alerts', true);
})->get();
foreach ($users as $user) {
$user->notify($notification);
}
}
}
Frost Warning Notification:
type = $type;
$this->temperature = $temperature;
$this->when = $when;
}
public function via($notifiable)
{
return ['mail', 'database'];
}
public function toMail($notifiable)
{
$subject = $this->type === 'freeze' ? '🧊 FREEZE WARNING' : '❄️ Frost Warning';
return (new MailMessage)
->subject($subject . ' - Blackberry Farm Alert')
->line("Weather alert for your blackberry farm!")
->line("Temperature: {$this->temperature}°F")
->line("Time: " . ($this->when === 'current' ? 'Now' : $this->when->format('M j, Y g:i A')))
->line('Recommended Actions:')
->line('• Cover sensitive plants')
->line('• Run irrigation if available')
->line('• Check plant protection measures')
->action('View Weather Dashboard', route('weather.dashboard'));
}
public function toArray($notifiable)
{
return [
'type' => 'weather_alert',
'alert_type' => $this->type,
'temperature' => $this->temperature,
'when' => $this->when,
'message' => "{$this->type} warning: {$this->temperature}°F"
];
}
}
Create automated task scheduling with smart reminders based on plant lifecycle and seasonal needs.
Task Reminder System:
# Create task reminder command
php artisan make:command SendTaskReminders
# Create task notification
php artisan make:notification TaskReminder
php artisan make:notification OverdueTaskAlert
Task Management Features:
class TaskReminderService
{
public function generateSeasonalTasks()
{
$currentMonth = now()->month;
$tasks = [];
switch ($currentMonth) {
case 3: // March
$tasks = [
['task' => 'Apply pre-emergent fertilizer', 'priority' => 'high'],
['task' => 'Prune winter-damaged canes', 'priority' => 'high'],
['task' => 'Check irrigation system', 'priority' => 'medium']
];
break;
case 6: // June
$tasks = [
['task' => 'Summer harvest preparation', 'priority' => 'high'],
['task' => 'Pest monitoring and treatment', 'priority' => 'medium'],
['task' => 'Weed control', 'priority' => 'medium']
];
break;
case 9: // September
$tasks = [
['task' => 'Fall fertilizer application', 'priority' => 'high'],
['task' => 'Air layering opportunity', 'priority' => 'high'],
['task' => 'Soil testing', 'priority' => 'medium']
];
break;
}
return $tasks;
}
public function checkPlantSpecificTasks()
{
$tasks = [];
// Check plants needing measurement
$plantsNeedingMeasurement = Plant::whereDoesntHave('measurements', function($query) {
$query->where('measurement_date', '>=', now()->subDays(30));
})->get();
foreach ($plantsNeedingMeasurement as $plant) {
$tasks[] = [
'plant_id' => $plant->plant_id,
'task' => 'Record growth measurements',
'priority' => 'medium',
'due_date' => now()->addDays(7)
];
}
// Check air layers ready for separation
$readyAirLayers = AirLayer::readyForSeparation()->get();
foreach ($readyAirLayers as $layer) {
$tasks[] = [
'air_layer_id' => $layer->layer_id,
'task' => 'Separate air layer from parent plant',
'priority' => 'high',
'due_date' => now()->addDays(3)
];
}
return $tasks;
}
}
Create automated equipment maintenance scheduling and alerts.
Equipment Maintenance System:
# Create maintenance alert command
php artisan make:command CheckEquipmentMaintenance
# Create maintenance notifications
php artisan make:notification MaintenanceDue
php artisan make:notification MaintenanceOverdue
Maintenance Alert Logic:
class EquipmentMaintenanceService
{
public function checkMaintenanceSchedules()
{
$equipment = Equipment::where('active', true)->get();
$alerts = [];
foreach ($equipment as $item) {
$maintenance = $this->calculateNextMaintenance($item);
if ($maintenance['status'] === 'overdue') {
$alerts[] = [
'type' => 'overdue',
'equipment' => $item,
'days_overdue' => $maintenance['days_overdue'],
'priority' => 'critical'
];
} elseif ($maintenance['status'] === 'due_soon') {
$alerts[] = [
'type' => 'due_soon',
'equipment' => $item,
'days_until_due' => $maintenance['days_until_due'],
'priority' => 'high'
];
}
}
return $alerts;
}
private function calculateNextMaintenance($equipment)
{
$lastMaintenance = $equipment->last_maintenance_date ?: $equipment->purchase_date;
// Define maintenance intervals by equipment type
$intervals = [
'irrigation' => 90, // days
'tractor' => 60,
'sprayer' => 30,
'harvesting' => 45
];
$interval = $intervals[$equipment->equipment_category] ?? 90;
$nextDue = $lastMaintenance->addDays($interval);
$daysUntilDue = now()->diffInDays($nextDue, false);
if ($daysUntilDue < 0) {
return [
'status' => 'overdue',
'days_overdue' => abs($daysUntilDue),
'next_due' => $nextDue
];
} elseif ($daysUntilDue <= 7) {
return [
'status' => 'due_soon',
'days_until_due' => $daysUntilDue,
'next_due' => $nextDue
];
}
return [
'status' => 'ok',
'days_until_due' => $daysUntilDue,
'next_due' => $nextDue
];
}
}
Create automated inventory monitoring with low stock and expiration alerts.
Inventory Alert System:
class InventoryAlertService
{
public function checkInventoryLevels()
{
$alerts = [];
// Check product inventory levels
$products = ValueAddProduct::all();
foreach ($products as $product) {
$inventory = $product->getCurrentInventory();
$minimumStock = $this->getMinimumStockLevel($product);
if ($inventory <= 0) {
$alerts[] = [
'type' => 'out_of_stock',
'product' => $product,
'current_stock' => $inventory,
'priority' => 'critical'
];
} elseif ($inventory <= $minimumStock) {
$alerts[] = [
'type' => 'low_stock',
'product' => $product,
'current_stock' => $inventory,
'minimum_stock' => $minimumStock,
'priority' => 'high'
];
}
}
// Check for expiring products
$expiringBatches = ProductionBatch::expiringWithin(7)->get();
foreach ($expiringBatches as $batch) {
$alerts[] = [
'type' => 'expiring_soon',
'batch' => $batch,
'days_until_expiration' => $batch->days_until_expiration,
'priority' => $batch->days_until_expiration <= 3 ? 'critical' : 'high'
];
}
return $alerts;
}
private function getMinimumStockLevel($product)
{
// Calculate minimum stock based on sales history
$avgMonthlySales = Sale::where('product_id', $product->product_id)
->where('sale_date', '>=', now()->subMonths(6))
->avg(\DB::raw('quantity'));
return max(ceil($avgMonthlySales * 0.5), 5); // 2-week safety stock, minimum 5 units
}
}
Implement email, SMS, and in-app notifications with user preferences.
User Notification Preferences:
# Create user preferences migration
php artisan make:migration add_notification_preferences_to_users_table
# Migration content:
Schema::table('users', function (Blueprint $table) {
$table->json('notification_preferences')->nullable();
$table->string('phone')->nullable();
$table->boolean('sms_enabled')->default(false);
});
Notification Preference Management:
class NotificationPreference extends Model
{
protected $fillable = [
'user_id', 'weather_alerts', 'task_reminders', 'equipment_maintenance',
'inventory_alerts', 'harvest_reminders', 'email_enabled', 'sms_enabled',
'push_enabled', 'alert_frequency'
];
protected $casts = [
'weather_alerts' => 'boolean',
'task_reminders' => 'boolean',
'equipment_maintenance' => 'boolean',
'inventory_alerts' => 'boolean',
'harvest_reminders' => 'boolean',
'email_enabled' => 'boolean',
'sms_enabled' => 'boolean',
'push_enabled' => 'boolean'
];
}
// Universal notification sending method
class NotificationService
{
public function sendAlert($user, $notification)
{
$preferences = $user->notificationPreferences;
$channels = [];
if ($preferences->email_enabled) {
$channels[] = 'mail';
}
if ($preferences->sms_enabled && $user->phone) {
$channels[] = 'sms';
}
if ($preferences->push_enabled) {
$channels[] = 'database';
}
// Respect quiet hours (10 PM to 6 AM)
$currentHour = now()->hour;
if ($currentHour >= 22 || $currentHour <= 6) {
// Only send critical alerts during quiet hours
if ($notification->priority !== 'critical') {
return;
}
}
$user->notify($notification);
}
}
Create dashboard for managing alerts and notification history.
Alert Dashboard Features:
// Alert Dashboard Controller
class AlertDashboardController extends Controller
{
public function index()
{
$activeAlerts = Alert::where('status', 'active')
->orderByDesc('created_at')
->get();
$recentNotifications = auth()->user()
->notifications()
->latest()
->limit(10)
->get();
$alertStats = [
'critical' => Alert::where('priority', 'critical')->where('status', 'active')->count(),
'high' => Alert::where('priority', 'high')->where('status', 'active')->count(),
'medium' => Alert::where('priority', 'medium')->where('status', 'active')->count(),
'resolved_today' => Alert::where('resolved_at', '>=', now()->startOfDay())->count()
];
return view('alerts.dashboard', compact('activeAlerts', 'recentNotifications', 'alertStats'));
}
public function acknowledge(Alert $alert)
{
$alert->update([
'acknowledged_at' => now(),
'acknowledged_by' => auth()->id()
]);
return response()->json(['status' => 'acknowledged']);
}
public function resolve(Alert $alert)
{
$alert->update([
'status' => 'resolved',
'resolved_at' => now(),
'resolved_by' => auth()->id()
]);
return redirect()->back()->with('success', 'Alert resolved successfully');
}
}
// Scheduled task to clean up old notifications
class CleanupNotifications extends Command
{
protected $signature = 'notifications:cleanup';
public function handle()
{
// Delete notifications older than 30 days
\DB::table('notifications')
->where('created_at', '<', now()->subDays(30))
->delete();
// Archive resolved alerts older than 90 days
Alert::where('status', 'resolved')
->where('resolved_at', '<', now()->subDays(90))
->delete();
$this->info('Notification cleanup completed');
}
}
Configure Laravel's task scheduler to run all alert checks automatically.
Task Scheduler Configuration (app/Console/Kernel.php):
protected function schedule(Schedule $schedule)
{
// Weather alerts - check every 30 minutes
$schedule->command('weather:check-alerts')->everyThirtyMinutes();
// Task reminders - daily at 7 AM
$schedule->command('tasks:send-reminders')->dailyAt('07:00');
// Equipment maintenance - weekly on Mondays
$schedule->command('equipment:check-maintenance')->weeklyOn(1, '09:00');
// Inventory alerts - daily at 6 AM
$schedule->command('inventory:check-levels')->dailyAt('06:00');
// Harvest window alerts - during harvest season, twice daily
$schedule->command('harvest:check-windows')
->twiceDaily(8, 16)
->when(function () {
$month = now()->month;
return in_array($month, [6, 7, 8, 9, 10]); // Harvest months
});
// Cleanup old notifications - weekly
$schedule->command('notifications:cleanup')->weekly();
// Generate daily farm report - daily at 5 AM
$schedule->command('reports:daily-summary')->dailyAt('05:00');
}
✅ Phase 6 Completion Checklist
- Weather alert system with frost/freeze warnings
- Automated task scheduling and reminders
- Equipment maintenance alert system
- Inventory level monitoring and alerts
- Multi-channel notifications (email, SMS, in-app)
- User notification preferences management
- Alert dashboard with acknowledgment system
- Scheduled task automation via cron
- Notification history and cleanup
- Priority-based alert filtering
🎯 Next Steps: Phase 7 Preparation
With Phase 6 complete, you're ready for Phase 7: Mobile App Development. The next phase will involve:
- Laravel API development for mobile
- Mobile app framework selection
- Native QR code scanning
- Offline data collection capabilities
- Data synchronization features
# Create database backup
mysqldump -u wwwhom8_main -p wwwhom8_blackberries > phase6_backup.sql
# Create git commit
git add .
git commit -m "Phase 6: Alerts & Notifications completed - weather alerts, task scheduling, equipment maintenance"
# Create git tag
git tag -a v6.0-phase6 -m "Phase 6: Alerts & Notifications completed"