heizung/app/Console/Commands/HealthCheck.php

75 lines
2.7 KiB
PHP

<?php
namespace App\Console\Commands;
use App\Models\Device;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Mail;
class HealthCheck extends Command
{
protected $signature = 'heizung:healthcheck
{--stale=5 : Minutes without data = alarm}
{--notify= : Email address for alerts}
{--sms= : Phone number for mail2sms}';
protected $description = 'Check device liveness and alert on state changes';
public function handle(): int
{
$stale = (int) $this->option('stale');
$email = $this->option('notify') ?: env('HEALTHCHECK_EMAIL');
$sms = $this->option('sms') ?: env('HEALTHCHECK_SMS');
$th = now()->subMinutes($stale);
$problems = 0;
foreach (Device::all() as $d) {
$down = !$d->last_seen_at || $d->last_seen_at->lt($th);
$prev = Cache::get("hc:d:{$d->id}", 'ok');
$new = $down ? 'down' : 'ok';
$this->line("$d->name: $new");
if ($new !== $prev) {
$this->notify($d, $new, $email, $sms);
Cache::put("hc:d:{$d->id}", $new, now()->addDays(30));
}
$problems += $down ? 1 : 0;
}
return $problems > 0 ? 1 : 0;
}
private function notify(Device $d, string $state, ?string $email, ?string $sms): void
{
if ($state === 'down') {
$subj = "[ALARM] Heizung {$d->name} offline";
$body = "'{$d->name}' seit ".($d->last_seen_at?->diffForHumans() ?? 'immer').
" keine Daten.\nLast seen: ".($d->last_seen_at?->toDateTimeString() ?? 'nie');
} else {
$subj = "[OK] Heizung {$d->name} wieder online";
$body = "'{$d->name}' liefert wieder Daten.\nLast seen: ".$d->last_seen_at?->toDateTimeString();
}
if ($email) $this->sendMail($email, $subj, $body);
if ($sms) $this->sendSms($sms, $subj, $body);
$this->warn("notify: $subj");
}
private function sendMail(string $to, string $subj, string $body): void
{
try {
Mail::raw($body, function ($m) use ($to, $subj) {
$m->to($to)->subject($subj);
});
} catch (\Throwable $e) {
$this->error("Mail failed: ".$e->getMessage());
}
}
private function sendSms(string $number, string $subj, string $body): void
{
// mail2sms Gateway: subject=Nummer, body=Text
try {
Mail::raw(substr($subj.' '.$body, 0, 140), function ($m) use ($number) {
$m->to('mail2sms@dimedtec.net')->subject($number);
});
} catch (\Throwable $e) {
$this->error("SMS failed: ".$e->getMessage());
}
}
}