authorize('viewAny', Device::class); $devices = Device::with(['zone', 'deviceType'])->orderBy('name')->get(); $statuses = $shadow->statuses($devices->pluck('external_id')->all()); $devices->each(fn (Device $d) => $d->live_status = $statuses[$d->external_id] ?? 'unknown'); return DeviceResource::collection($devices); } public function show(Device $device, DeviceShadow $shadow, ClickHouseClient $clickHouse) { $this->authorize('view', $device); $device->load(['zone', 'deviceType']); $snapshot = $shadow->snapshot($device->external_id); $device->live_status = $snapshot['status']; $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 (new DeviceResource($device))->additional([ 'shadow' => [ 'last_seen' => $snapshot['last_seen'], 'desired_state' => $snapshot['desired_state'], 'reported_state' => $snapshot['reported_state'], ], 'telemetry' => $telemetry, ]); } public function store(DeviceRequest $request) { $device = new Device($request->validated()); $device->user()->associate($request->user()); $device->save(); return (new DeviceResource($device))->response()->setStatusCode(201); } public function update(DeviceRequest $request, Device $device) { $device->update($request->validated()); return new DeviceResource($device); } public function destroy(Device $device) { $this->authorize('delete', $device); $device->delete(); return response()->noContent(); } 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) { if (! $result['success']) { return response()->json(['message' => $result['error'] ?? 'Команда не выполнена.'], 422); } return response()->json(['message' => 'Команда отправлена.']); } }