Fixed performance
This commit is contained in:
parent
5f13dc83b6
commit
0ae1bdf76b
@ -70,8 +70,8 @@ final class CalendarController extends AbstractController
|
|||||||
$icsContent = $this->calendarExportService->generateIcsContent($days, $entityHistory, $absences);
|
$icsContent = $this->calendarExportService->generateIcsContent($days, $entityHistory, $absences);
|
||||||
|
|
||||||
$response = new Response($icsContent);
|
$response = new Response($icsContent);
|
||||||
//$response->headers->set('Content-Type', 'text/calendar; charset=utf-8');
|
$response->headers->set('Content-Type', 'text/calendar; charset=utf-8');
|
||||||
//$response->headers->set('Content-Disposition', 'attachment; filename="work_calendar.ics"');
|
$response->headers->set('Content-Disposition', 'attachment; filename="work_calendar.ics"');
|
||||||
|
|
||||||
return $response;
|
return $response;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,22 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace App\Core\Services\Home;
|
|
||||||
|
|
||||||
use DateTimeImmutable;
|
|
||||||
|
|
||||||
interface HomeEntityInterface
|
|
||||||
{
|
|
||||||
public function getId(): string;
|
|
||||||
|
|
||||||
public function getState(): mixed;
|
|
||||||
|
|
||||||
public function getName(): string;
|
|
||||||
|
|
||||||
public function getType(): HomeEntityType;
|
|
||||||
|
|
||||||
public function getLastChanged(): DateTimeImmutable;
|
|
||||||
|
|
||||||
public function getLastUpdated(): DateTimeImmutable;
|
|
||||||
}
|
|
||||||
@ -1,14 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace App\Core\Services\Home;
|
|
||||||
|
|
||||||
interface HomeServiceInterface
|
|
||||||
{
|
|
||||||
public function findEntity(string $entityId): HomeEntityInterface;
|
|
||||||
|
|
||||||
public function findAllEntities(): array;
|
|
||||||
|
|
||||||
public function callService(string $service, array $data = []): array;
|
|
||||||
}
|
|
||||||
@ -5,7 +5,6 @@ declare(strict_types=1);
|
|||||||
namespace App\HomeAssistant;
|
namespace App\HomeAssistant;
|
||||||
|
|
||||||
use function explode;
|
use function explode;
|
||||||
use function in_array;
|
|
||||||
|
|
||||||
final readonly class EntityState
|
final readonly class EntityState
|
||||||
{
|
{
|
||||||
@ -30,17 +29,6 @@ final readonly class EntityState
|
|||||||
$data['context'] ?? null,
|
$data['context'] ?? null,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isOn(): bool
|
|
||||||
{
|
|
||||||
return in_array($this->state, ['on', 'home', 'open', 'unlocked', 'active'], true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function isOff(): bool
|
|
||||||
{
|
|
||||||
return in_array($this->state, ['off', 'away', 'closed', 'locked', 'inactive'], true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getDomain(): string
|
public function getDomain(): string
|
||||||
{
|
{
|
||||||
$parts = explode('.', $this->entityId, 2);
|
$parts = explode('.', $this->entityId, 2);
|
||||||
|
|||||||
@ -5,7 +5,6 @@ declare(strict_types=1);
|
|||||||
namespace App\HomeAssistant;
|
namespace App\HomeAssistant;
|
||||||
|
|
||||||
use DateTimeInterface;
|
use DateTimeInterface;
|
||||||
use function explode;
|
|
||||||
|
|
||||||
use Symfony\Component\DependencyInjection\Attribute\Autowire;
|
use Symfony\Component\DependencyInjection\Attribute\Autowire;
|
||||||
use Symfony\Contracts\HttpClient\HttpClientInterface;
|
use Symfony\Contracts\HttpClient\HttpClientInterface;
|
||||||
@ -29,11 +28,6 @@ final readonly class HomeAssistantClient
|
|||||||
return $this->request('GET', '/api/states');
|
return $this->request('GET', '/api/states');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getServices(): array
|
|
||||||
{
|
|
||||||
return $this->request('GET', '/api/services');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getEntityState(string $entityId): array
|
public function getEntityState(string $entityId): array
|
||||||
{
|
{
|
||||||
return $this->request('GET', "/api/states/{$entityId}");
|
return $this->request('GET', "/api/states/{$entityId}");
|
||||||
@ -49,25 +43,6 @@ final readonly class HomeAssistantClient
|
|||||||
return $this->request('GET', "/api/history/period/{$startDate->format('Y-m-d\TH:i:s\Z')}?{$queryParams}")[0];
|
return $this->request('GET', "/api/history/period/{$startDate->format('Y-m-d\TH:i:s\Z')}?{$queryParams}")[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function callService(string $domain, string $service, array $data = []): array
|
|
||||||
{
|
|
||||||
return $this->request('POST', "/api/services/{$domain}/{$service}", $data);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function turnOn(string $entityId): array
|
|
||||||
{
|
|
||||||
$domain = explode('.', $entityId)[0];
|
|
||||||
|
|
||||||
return $this->callService($domain, 'turn_on', ['entity_id' => $entityId]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function turnOff(string $entityId): array
|
|
||||||
{
|
|
||||||
$domain = explode('.', $entityId)[0];
|
|
||||||
|
|
||||||
return $this->callService($domain, 'turn_off', ['entity_id' => $entityId]);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function request(string $method, string $endpoint, array $data = []): array
|
private function request(string $method, string $endpoint, array $data = []): array
|
||||||
{
|
{
|
||||||
$options = [
|
$options = [
|
||||||
|
|||||||
@ -4,64 +4,13 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace App\HomeAssistant;
|
namespace App\HomeAssistant;
|
||||||
|
|
||||||
use App\Core\Services\Home\HomeEntityInterface;
|
final readonly class HomeAssistantHomeService
|
||||||
use App\Core\Services\Home\HomeEntityType;
|
|
||||||
use App\Core\Services\Home\HomeServiceInterface;
|
|
||||||
|
|
||||||
use function array_filter;
|
|
||||||
use function array_keys;
|
|
||||||
use function array_map;
|
|
||||||
use function explode;
|
|
||||||
use function str_contains;
|
|
||||||
|
|
||||||
final readonly class HomeAssistantHomeService implements HomeServiceInterface
|
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private HomeAssistantClient $client,
|
private HomeAssistantClient $client,
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
public function findEntity(string $entityId): HomeEntityInterface
|
|
||||||
{
|
|
||||||
$entityState = $this->getEntityState($entityId);
|
|
||||||
|
|
||||||
return new HomeAssistantEntity($entityState);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function findAllEntities(): array
|
|
||||||
{
|
|
||||||
$states = $this->getAllEntityStates();
|
|
||||||
|
|
||||||
if (empty($states)) {
|
|
||||||
throw new HomeAssistantException('No entities found');
|
|
||||||
}
|
|
||||||
|
|
||||||
$entities = [];
|
|
||||||
foreach ($states as $state) {
|
|
||||||
$type = HomeEntityType::tryFrom($state->getDomain());
|
|
||||||
|
|
||||||
if ($type === null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$entities[] = new HomeAssistantEntity($state);
|
|
||||||
}
|
|
||||||
|
|
||||||
return array_filter($entities, fn (HomeEntityInterface $entity) => $entity->getType() === HomeEntityType::LIGHT);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function callService(string $service, array $data = []): array
|
|
||||||
{
|
|
||||||
// Extract domain and service name from the service string
|
|
||||||
if (str_contains($service, '.')) {
|
|
||||||
[$domain, $serviceName] = explode('.', $service, 2);
|
|
||||||
|
|
||||||
return $this->client->callService($domain, $serviceName, $data);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new HomeAssistantException("Invalid service format. Expected 'domain.service'");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return EntityState[]
|
* @return EntityState[]
|
||||||
*/
|
*/
|
||||||
@ -81,41 +30,4 @@ final readonly class HomeAssistantHomeService implements HomeServiceInterface
|
|||||||
|
|
||||||
return EntityState::fromArray($state);
|
return EntityState::fromArray($state);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return EntityState[]
|
|
||||||
*/
|
|
||||||
public function getEntitiesByDomain(string $domain): array
|
|
||||||
{
|
|
||||||
$allStates = $this->getAllEntityStates();
|
|
||||||
|
|
||||||
return array_filter(
|
|
||||||
$allStates,
|
|
||||||
static fn (EntityState $state): bool => $state->getDomain() === $domain,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function turnOn(string $entityId): EntityState
|
|
||||||
{
|
|
||||||
$this->client->turnOn($entityId);
|
|
||||||
|
|
||||||
return $this->getEntityState($entityId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function turnOff(string $entityId): EntityState
|
|
||||||
{
|
|
||||||
$this->client->turnOff($entityId);
|
|
||||||
|
|
||||||
return $this->getEntityState($entityId);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return string[]
|
|
||||||
*/
|
|
||||||
public function getAvailableDomains(): array
|
|
||||||
{
|
|
||||||
$services = $this->client->getServices();
|
|
||||||
|
|
||||||
return array_keys($services);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -21,6 +21,27 @@ final class CalendarExportService
|
|||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array<array{
|
||||||
|
* date: DateTimeImmutable,
|
||||||
|
* weekday: int,
|
||||||
|
* weekdayName: string,
|
||||||
|
* weekNumber: int,
|
||||||
|
* class: string,
|
||||||
|
* isFtk: bool,
|
||||||
|
* isUrlaub: bool
|
||||||
|
* }> $days
|
||||||
|
* @param array<array{
|
||||||
|
* last_changed: string,
|
||||||
|
* state: string
|
||||||
|
* }> $entityHistory
|
||||||
|
*
|
||||||
|
* @param array<array{
|
||||||
|
* date: DateTimeImmutable,
|
||||||
|
* isFtk: bool,
|
||||||
|
* isUrlaub: bool
|
||||||
|
* }> $absences
|
||||||
|
*/
|
||||||
public function generateIcsContent(array $days, array $entityHistory, array $absences): string
|
public function generateIcsContent(array $days, array $entityHistory, array $absences): string
|
||||||
{
|
{
|
||||||
$icsContent = [
|
$icsContent = [
|
||||||
@ -38,21 +59,16 @@ final class CalendarExportService
|
|||||||
}
|
}
|
||||||
|
|
||||||
$dayDate = clone $day['date'];
|
$dayDate = clone $day['date'];
|
||||||
$startOfDay = DateTimeImmutable::createFromInterface($dayDate)->setTime(0, 0, 0);
|
|
||||||
$typicalEndOfDay = DateTimeImmutable::createFromInterface($dayDate)->setTime(17, 30, 0);
|
|
||||||
|
|
||||||
$filteredEntityHistory = array_filter($entityHistory, function (array $state) use ($startOfDay, $typicalEndOfDay) {
|
$filteredEntityHistory = array_filter($entityHistory, function (array $state) use ($dayDate) {
|
||||||
return $state['last_changed'] >= $startOfDay && $state['last_changed'] <= $typicalEndOfDay;
|
return new DateTimeImmutable($state['last_changed']) >= $dayDate->setTime(0, 0, 0) && new DateTimeImmutable($state['last_changed']) <= $dayDate->setTime(23, 59, 59);
|
||||||
});
|
});
|
||||||
|
|
||||||
$startAndEndTimes = $this->getWorkTimesFromHomeAssistant($filteredEntityHistory, $dayDate);
|
$startAndEndTimes = $this->getWorkTimesFromHomeAssistant($filteredEntityHistory);
|
||||||
|
|
||||||
if ($startAndEndTimes === null) {
|
if ($startAndEndTimes === null || count($filteredEntityHistory) === 0) {
|
||||||
$eventStart = clone $day['date'];
|
$eventStart = $dayDate->setTime(9, 0, 0);
|
||||||
$eventStart->setTime(9, 0, 0);
|
$eventEnd = $dayDate->setTime(18, 0, 0);
|
||||||
|
|
||||||
$eventEnd = clone $day['date'];
|
|
||||||
$eventEnd->setTime(17, 30, 0);
|
|
||||||
} else {
|
} else {
|
||||||
[$eventStart, $eventEnd] = $startAndEndTimes;
|
[$eventStart, $eventEnd] = $startAndEndTimes;
|
||||||
}
|
}
|
||||||
@ -80,17 +96,28 @@ final class CalendarExportService
|
|||||||
return implode("\r\n", $icsContent);
|
return implode("\r\n", $icsContent);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function getWorkTimesFromHomeAssistant(array $stateHistory, DateTimeInterface $date): ?array
|
/**
|
||||||
|
* @param array<array{
|
||||||
|
* last_changed: string,
|
||||||
|
* state: string
|
||||||
|
* }> $states
|
||||||
|
*
|
||||||
|
* @return array{
|
||||||
|
* start: DateTimeImmutable,
|
||||||
|
* end: DateTimeImmutable
|
||||||
|
* }|null
|
||||||
|
*/
|
||||||
|
private function getWorkTimesFromHomeAssistant(array $states): ?array
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
if (count($states) === 0) {
|
||||||
if (empty($stateHistory) || empty($stateHistory[0])) {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$states = array_values($states);
|
||||||
|
|
||||||
$startTime = null;
|
$startTime = null;
|
||||||
$endTime = null;
|
$endTime = null;
|
||||||
$states = $stateHistory[0];
|
|
||||||
|
|
||||||
foreach ($states as $i => $state) {
|
foreach ($states as $i => $state) {
|
||||||
if ($state['state'] === self::OFFICE_STATE) {
|
if ($state['state'] === self::OFFICE_STATE) {
|
||||||
|
|||||||
@ -7,6 +7,7 @@ use DateTime;
|
|||||||
use DateTimeImmutable;
|
use DateTimeImmutable;
|
||||||
use DatePeriod;
|
use DatePeriod;
|
||||||
use DateInterval;
|
use DateInterval;
|
||||||
|
use DateTimeInterface;
|
||||||
use IntlDateFormatter;
|
use IntlDateFormatter;
|
||||||
|
|
||||||
final class CalendarService
|
final class CalendarService
|
||||||
@ -25,6 +26,19 @@ final class CalendarService
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $startDate
|
||||||
|
* @param string $endDate
|
||||||
|
* @return array<array{
|
||||||
|
* date: DateTimeImmutable,
|
||||||
|
* weekday: int,
|
||||||
|
* weekdayName: string,
|
||||||
|
* weekNumber: int,
|
||||||
|
* class: string,
|
||||||
|
* isFtk: bool,
|
||||||
|
* isUrlaub: bool
|
||||||
|
* }>
|
||||||
|
*/
|
||||||
public function getAllDays(string $startDate, string $endDate): array
|
public function getAllDays(string $startDate, string $endDate): array
|
||||||
{
|
{
|
||||||
$period = new DatePeriod(
|
$period = new DatePeriod(
|
||||||
@ -37,7 +51,7 @@ final class CalendarService
|
|||||||
foreach ($period as $date) {
|
foreach ($period as $date) {
|
||||||
$dateObj = DateTimeImmutable::createFromInterface($date);
|
$dateObj = DateTimeImmutable::createFromInterface($date);
|
||||||
$days[] = [
|
$days[] = [
|
||||||
'date' => $date,
|
'date' => $dateObj,
|
||||||
'weekday' => (int) $dateObj->format('N'),
|
'weekday' => (int) $dateObj->format('N'),
|
||||||
'weekdayName' => $this->formatter->format($date),
|
'weekdayName' => $this->formatter->format($date),
|
||||||
'weekNumber' => (int) $dateObj->format('W'),
|
'weekNumber' => (int) $dateObj->format('W'),
|
||||||
@ -95,7 +109,7 @@ final class CalendarService
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function isSameDay(DateTime $date1, DateTime $date2): bool
|
private function isSameDay(DateTimeInterface $date1, DateTimeInterface $date2): bool
|
||||||
{
|
{
|
||||||
return $date1->format('Y-m-d') === $date2->format('Y-m-d');
|
return $date1->format('Y-m-d') === $date2->format('Y-m-d');
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user