getUser(); $devices = $em->getRepository(Device::class)->findBy(['user' => $user]); return $this->json(array_map($this->serialize(...), $devices)); } #[Route('/{id}', name: 'api_device_update', methods: ['PATCH'])] public function update(int $id, Request $request, EntityManagerInterface $em): JsonResponse { /** @var User $user */ $user = $this->getUser(); $device = $em->getRepository(Device::class)->findOneBy(['id' => $id, 'user' => $user]); if (!$device) { return $this->json(['error' => 'Device not found'], Response::HTTP_NOT_FOUND); } $body = json_decode($request->getContent(), true) ?? []; if (isset($body['name'])) { $name = trim((string) $body['name']); if (empty($name)) { return $this->json(['error' => 'Name cannot be empty'], Response::HTTP_UNPROCESSABLE_ENTITY); } $device->setName($name); } if (isset($body['orientation'])) { $orientation = Orientation::tryFrom($body['orientation']); if (!$orientation) { return $this->json(['error' => 'Invalid orientation'], Response::HTTP_UNPROCESSABLE_ENTITY); } $device->setOrientation($orientation); } if (isset($body['rotationIntervalMinutes'])) { $device->setRotationIntervalMinutes(max(1, (int) $body['rotationIntervalMinutes'])); } if (array_key_exists('wakeHour', $body)) { $device->setWakeHour($body['wakeHour'] === null ? null : (int) $body['wakeHour']); } if (isset($body['timezone'])) { try { new \DateTimeZone((string) $body['timezone']); $device->setTimezone((string) $body['timezone']); } catch (\Exception) { return $this->json(['error' => 'Invalid timezone identifier'], Response::HTTP_UNPROCESSABLE_ENTITY); } } if (isset($body['uniquenessWindow'])) { $device->setUniquenessWindow(max(1, (int) $body['uniquenessWindow'])); } $em->flush(); return $this->json($this->serialize($device)); } #[Route('/{id}/lock', name: 'api_device_lock', methods: ['PUT'])] public function lock(int $id, Request $request, EntityManagerInterface $em): JsonResponse { /** @var User $user */ $user = $this->getUser(); $device = $em->getRepository(Device::class)->findOneBy(['id' => $id, 'user' => $user]); if (!$device) { return $this->json(['error' => 'Device not found'], Response::HTTP_NOT_FOUND); } $body = json_decode($request->getContent(), true) ?? []; $imageId = $body['imageId'] ?? null; if (!$imageId) { return $this->json(['error' => 'imageId required'], Response::HTTP_UNPROCESSABLE_ENTITY); } $image = $em->getRepository(Image::class)->find($imageId); if (!$image || $image->getUser() !== $user) { return $this->json(['error' => 'Image not found'], Response::HTTP_NOT_FOUND); } if (!$image->isApprovedForDevice($device)) { return $this->json(['error' => 'Image is not approved for this device'], Response::HTTP_UNPROCESSABLE_ENTITY); } $device->setLockedImage($image); $em->flush(); return $this->json($this->serialize($device)); } #[Route('/{id}/lock', name: 'api_device_unlock', methods: ['DELETE'])] public function unlock(int $id, EntityManagerInterface $em): JsonResponse { /** @var User $user */ $user = $this->getUser(); $device = $em->getRepository(Device::class)->findOneBy(['id' => $id, 'user' => $user]); if (!$device) { return $this->json(['error' => 'Device not found'], Response::HTTP_NOT_FOUND); } $device->setLockedImage(null); $em->flush(); return $this->json($this->serialize($device)); } private function serialize(Device $d): array { return [ 'id' => $d->getId(), 'mac' => $d->getMac(), 'name' => $d->getName(), 'orientation' => $d->getOrientation()->value, 'rotationIntervalMinutes' => $d->getRotationIntervalMinutes(), 'wakeHour' => $d->getWakeHour(), 'timezone' => $d->getTimezone(), 'uniquenessWindow' => $d->getUniquenessWindow(), 'linkedAt' => $d->getLinkedAt()->format(\DateTimeInterface::ATOM), 'lastSeenAt' => $d->getLastSeenAt()?->format(\DateTimeInterface::ATOM), 'lockedImageId' => $d->getLockedImage()?->getId(), ]; } }