homeos/app/Services/DeviceCommandService.php

72 lines
2.2 KiB
PHP

<?php
namespace App\Services;
use App\Models\Command;
use App\Models\Device;
use App\Models\Entity;
use App\Support\Drivers\DeviceDriver;
use App\Support\Drivers\ShellyMqttDriver;
use Illuminate\Support\Facades\Auth;
/**
* The one path from UI/automations to a device (H1). Resolves the driver, executes the
* command and writes an audit row to `commands` — always, success or failure.
*/
class DeviceCommandService
{
public function toggle(Entity $entity, string $source = 'user'): Command
{
$current = (bool) data_get($entity->state?->state, 'on', false);
return $this->setOn($entity, ! $current, $source);
}
public function setOn(Entity $entity, bool $on, string $source = 'user'): Command
{
return $this->run(
$entity->device,
$entity,
$on ? 'turn_on' : 'turn_off',
['on' => $on],
$source,
fn (DeviceDriver $driver) => $on ? $driver->turnOn($entity) : $driver->turnOff($entity),
);
}
public function reboot(Device $device, string $source = 'user'): Command
{
return $this->run($device, null, 'reboot', [], $source, fn (DeviceDriver $driver) => $driver->reboot($device));
}
private function run(Device $device, ?Entity $entity, string $command, array $payload, string $source, callable $action): Command
{
$result = 'ok';
try {
$action($this->driverFor($device));
} catch (\Throwable $e) {
report($e);
$result = 'error: '.$e->getMessage();
}
return Command::create([
'device_id' => $device->id,
'entity_id' => $entity?->id,
'source' => $source,
'user_id' => Auth::id(),
'command' => $command,
'payload' => $payload,
'result' => $result,
]);
}
private function driverFor(Device $device): DeviceDriver
{
return match (true) {
$device->vendor === 'Shelly' && $device->protocol === 'mqtt' => app(ShellyMqttDriver::class),
default => throw new \RuntimeException("No driver for {$device->vendor}/{$device->protocol}."),
};
}
}