Files
api/services/nginx/app/classes/gateway_shelly_transport.php
T
Jeppe Bundgaard feb9aac4f7 Handle relay command job timeouts for edge gateways
- Introduce `expireTimedOutRelayStatusCommandJobs` to clean up long-pending relay status command jobs.
- Add `TIMED_OUT` status for relay command jobs and incorporate it into job status evaluations.
- Refactor command job finalization to support timeout-specific error messaging.
- Improve handling of fast-path failures in edge broker commands.
2026-04-27 16:47:09 +02:00

138 lines
4.2 KiB
PHP

<?php
namespace classes;
require_once WD . '/interfaces/shelly_transport_i.php';
use Exception;
use interfaces\shelly_transport_i;
class gateway_shelly_transport implements shelly_transport_i
{
public function __construct(
private readonly ?edge_gateway_manager $manager = null,
private readonly bool $localOnly = false
)
{
}
public function requireModuleEnabled(): void
{
// Gateway mode is department-scoped and does not depend on the Shelly cloud module flag.
}
public function requireValidSecretKey(): void
{
// Gateway mode does not use the Shelly cloud auth key.
}
/**
* @throws Exception
*/
public function sendPostRequest(string $endpoint, array $data, ?int $department_id = null): array|object|null
{
if ($department_id === null || $department_id <= 0) {
throw new Exception('A department_id is required for gateway Shelly transport');
}
return match ($endpoint) {
'/v2/devices/api/get' => $this->handleGetStates($department_id, $data),
'/v2/devices/api/set/switch' => $this->handleSetSwitch($department_id, $data),
default => throw new Exception('Unsupported gateway Shelly transport endpoint: ' . $endpoint),
};
}
/**
* @return array<int,array<string,mixed>>
* @throws Exception
*/
private function handleGetStates(int $departmentId, array $data): array
{
$ids = array_values(array_filter(
array_map(static fn(mixed $value): string => trim((string)$value), (array)($data['ids'] ?? [])),
static fn(string $value): bool => $value !== ''
));
$result = [];
foreach ($ids as $logicalRelayId) {
$status = $this->localOnly
? $this->manager()->dispatchRelayStatusLocalOnly($departmentId, $logicalRelayId)
: $this->manager()->dispatchRelayStatus($departmentId, $logicalRelayId);
$result[] = $this->normalizeRelayPayload($logicalRelayId, $status);
}
return $result;
}
/**
* @throws Exception
*/
private function handleSetSwitch(int $departmentId, array $data): array
{
$logicalRelayId = trim((string)($data['id'] ?? ''));
if ($logicalRelayId === '') {
throw new Exception('Shelly gateway switch requests require an id');
}
$toggleAfter = $this->normalizeToggleAfter(
$data['toggle_after'] ?? $data['toggleAfter'] ?? $data['timer'] ?? null
);
$status = $this->localOnly
? $this->manager()->dispatchRelaySwitchLocalOnlyWithTimer(
$departmentId,
$logicalRelayId,
(bool)($data['on'] ?? false),
$toggleAfter
)
: $this->manager()->dispatchRelaySwitchWithTimer(
$departmentId,
$logicalRelayId,
(bool)($data['on'] ?? false),
$toggleAfter
);
return [$this->normalizeRelayPayload($logicalRelayId, $status)];
}
/**
* @param array<string,mixed> $status
* @return array<string,mixed>
*/
private function normalizeRelayPayload(string $logicalRelayId, array $status): array
{
$on = (bool)($status['on'] ?? $status['output'] ?? false);
$online = (bool)($status['online'] ?? true);
return [
'id' => $logicalRelayId,
'relay_id' => $logicalRelayId,
'online' => $online,
'on' => $on,
'status' => [
'switch:0' => [
'output' => $on,
],
],
'binding' => (array)($status['binding'] ?? []),
'execution' => (array)($status['execution'] ?? []),
'raw' => (array)($status['raw'] ?? []),
];
}
private function manager(): edge_gateway_manager
{
return $this->manager ?? new edge_gateway_manager();
}
private function normalizeToggleAfter(mixed $toggleAfter): ?int
{
if ($toggleAfter === null || $toggleAfter === '') {
return null;
}
$seconds = (int)$toggleAfter;
return $seconds > 0 ? $seconds : null;
}
}