76 lines
2.2 KiB
PHP
76 lines
2.2 KiB
PHP
<?php
|
|
|
|
namespace App\Console\Commands;
|
|
|
|
use App\Events\PresenceChanged;
|
|
use App\Models\Person;
|
|
use App\Services\UnifiClient;
|
|
use Illuminate\Console\Command;
|
|
|
|
/**
|
|
* Presence sweep. "home" immediately on association; "away" only after a debounce
|
|
* (iPhone WLAN sleep causes false-aways otherwise). Never hangs a security automation
|
|
* on a single raw signal.
|
|
*/
|
|
class PresencePollCommand extends Command
|
|
{
|
|
protected $signature = 'presence:poll';
|
|
|
|
protected $description = 'Poll UniFi for client presence and update persons.';
|
|
|
|
private const AWAY_DEBOUNCE_MINUTES = 8;
|
|
|
|
public function handle(UnifiClient $unifi): int
|
|
{
|
|
if (! $unifi->isConfigured()) {
|
|
$this->warn('UniFi is not configured (UNIFI_* env).');
|
|
|
|
return self::SUCCESS;
|
|
}
|
|
|
|
try {
|
|
$connected = array_flip($unifi->connectedMacs());
|
|
} catch (\Throwable $e) {
|
|
report($e);
|
|
$this->error('UniFi poll failed: '.$e->getMessage());
|
|
|
|
return self::FAILURE;
|
|
}
|
|
|
|
$this->info('UniFi active clients: '.count($connected));
|
|
$debounce = now()->subMinutes(self::AWAY_DEBOUNCE_MINUTES);
|
|
|
|
foreach (Person::whereNotNull('mac')->get() as $person) {
|
|
$home = isset($connected[strtolower($person->mac)]);
|
|
|
|
if ($home) {
|
|
$person->last_seen_home_at = now();
|
|
if ($person->presence !== 'home') {
|
|
$this->markPresence($person, 'home');
|
|
} else {
|
|
$person->save();
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
// Not associated: flip to away only once the debounce has elapsed.
|
|
$seenLongAgo = $person->last_seen_home_at === null || $person->last_seen_home_at->lt($debounce);
|
|
if ($person->presence !== 'away' && $seenLongAgo) {
|
|
$this->markPresence($person, 'away');
|
|
}
|
|
}
|
|
|
|
return self::SUCCESS;
|
|
}
|
|
|
|
private function markPresence(Person $person, string $presence): void
|
|
{
|
|
$person->presence = $presence;
|
|
$person->presence_changed_at = now();
|
|
$person->save();
|
|
|
|
PresenceChanged::dispatch($person->uuid);
|
|
}
|
|
}
|