authorize('viewAny', Device::class); $devices = Device::with(['zone', 'deviceType'])->orderBy('name')->get(); $statuses = $shadow->statuses($devices->pluck('external_id')->all()); return view('devices.index', compact('devices', 'statuses')); } public function show(Device $device, DeviceShadow $shadow, ClickHouseClient $clickHouse) { $this->authorize('view', $device); $device->load(['zone', 'deviceType']); $telemetry = []; if ($device->deviceType->category->value === 'sensor') { $telemetry = $clickHouse->query( 'SELECT sensor_type, value, recorded_at FROM telemetry WHERE device_id = {device_id:String} ORDER BY recorded_at DESC LIMIT 50', ['device_id' => $device->external_id], ); } return view('devices.show', [ 'device' => $device, 'shadow' => $shadow->snapshot($device->external_id), 'telemetry' => $telemetry, ]); } public function create() { $this->authorize('create', Device::class); return view('devices.create', $this->formOptions()); } public function store(DeviceRequest $request) { $device = new Device($request->validated()); $device->user()->associate($request->user()); $device->save(); return redirect()->route('devices.index')->with('status', 'Устройство добавлено.'); } public function edit(Device $device) { $this->authorize('update', $device); return view('devices.edit', ['device' => $device, ...$this->formOptions()]); } public function update(DeviceRequest $request, Device $device) { $device->update($request->validated()); return redirect()->route('devices.index')->with('status', 'Устройство обновлено.'); } public function destroy(Device $device) { $this->authorize('delete', $device); $device->delete(); return redirect()->route('devices.index')->with('status', 'Устройство удалено.'); } public function turnOn(Device $device, DeviceControlClient $client) { $this->authorize('update', $device); $this->ensureCapability($device, 'turn_on'); return $this->respondToCommand($client->turnOn($device->external_id)); } public function turnOff(Device $device, DeviceControlClient $client) { $this->authorize('update', $device); $this->ensureCapability($device, 'turn_off'); return $this->respondToCommand($client->turnOff($device->external_id)); } public function setLevel(Request $request, Device $device, DeviceControlClient $client) { $this->authorize('update', $device); $this->ensureCapability($device, 'set_level'); $validated = $request->validate(['level' => ['required', 'numeric']]); return $this->respondToCommand($client->setLevel($device->external_id, (float) $validated['level'])); } private function ensureCapability(Device $device, string $capability): void { abort_unless( in_array($capability, $device->deviceType->capabilities ?? [], true), 422, "Устройство не поддерживает действие «{$capability}».", ); } /** * @param array{success: bool, error: ?string} $result */ private function respondToCommand(array $result) { return back()->with( $result['success'] ? 'status' : 'error', $result['success'] ? 'Команда отправлена.' : ($result['error'] ?? 'Команда не выполнена.'), ); } /** * @return array */ private function formOptions(): array { return [ 'zones' => Zone::orderBy('name')->get(), 'deviceTypes' => DeviceType::orderBy('code')->get(), ]; } }