86 lines
3.2 KiB
PHP
86 lines
3.2 KiB
PHP
<?php
|
|
|
|
namespace App\Services;
|
|
|
|
use App\Models\Device;
|
|
use Illuminate\Support\Collection;
|
|
|
|
/**
|
|
* Aggregates the current home state (all devices/entities, incl. those with no room)
|
|
* into a summary and a warning list. Shared by the Dashboard and the Warnings modal.
|
|
*/
|
|
class HomeStatus
|
|
{
|
|
private const LOW_BATTERY = 20;
|
|
|
|
public function __construct(private readonly SystemHealth $health) {}
|
|
|
|
/** Every device (including room-less ones), eager-loaded for aggregation + display. */
|
|
public function devices(): Collection
|
|
{
|
|
return Device::query()
|
|
->with(['entities.state', 'room'])
|
|
->orderBy('name')
|
|
->get();
|
|
}
|
|
|
|
public function summary(Collection $devices): array
|
|
{
|
|
$entities = $devices->flatMap->entities;
|
|
|
|
return [
|
|
'devices_online' => $devices->filter->isOnline()->count(),
|
|
'devices_total' => $devices->count(),
|
|
'lights_on' => $entities->filter(fn ($e) => in_array($e->type, ['light', 'switch']) && data_get($e->state, 'state.on') === true)->count(),
|
|
'contacts_open' => $entities->filter(fn ($e) => $e->type === 'contact' && data_get($e->state, 'state.open') === true)->count(),
|
|
'low_battery' => $entities->filter(fn ($e) => $e->type === 'battery' && (int) data_get($e->state, 'state.percent', 100) < self::LOW_BATTERY)->count(),
|
|
];
|
|
}
|
|
|
|
/** @return array<int, array{level:string,icon:string,title:string,meta:?string,link:?string}> */
|
|
public function warnings(Collection $devices): array
|
|
{
|
|
$warnings = [];
|
|
|
|
// Iterate devices (already loaded) → entities so we never lazy-load $entity->device (no N+1).
|
|
foreach ($devices as $device) {
|
|
if (! $device->isOnline()) {
|
|
$warnings[] = [
|
|
'level' => 'offline', 'icon' => 'devices',
|
|
'title' => $device->name, 'meta' => __('devices.offline'), 'link' => null,
|
|
];
|
|
}
|
|
|
|
foreach ($device->entities as $entity) {
|
|
if ($entity->type === 'contact' && data_get($entity->state, 'state.open') === true) {
|
|
$warnings[] = [
|
|
'level' => 'warning', 'icon' => 'window',
|
|
'title' => $device->name, 'meta' => __('devices.open'), 'link' => null,
|
|
];
|
|
}
|
|
|
|
if ($entity->type === 'battery') {
|
|
$percent = (int) data_get($entity->state, 'state.percent', 100);
|
|
if ($percent < self::LOW_BATTERY) {
|
|
$warnings[] = [
|
|
'level' => 'warning', 'icon' => 'alert',
|
|
'title' => $device->name,
|
|
'meta' => __('devices.battery').': '.$percent.'%', 'link' => null,
|
|
];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (! $this->health->allOnline()) {
|
|
$warnings[] = [
|
|
'level' => 'warning', 'icon' => 'activity',
|
|
'title' => __('dashboard.warn_host'), 'meta' => __('dashboard.warn_host_meta'),
|
|
'link' => route('host'),
|
|
];
|
|
}
|
|
|
|
return $warnings;
|
|
}
|
|
}
|