Update relay-handling logic and test assertions for device binding and local IP resolution

- Correct test cases to ensure proper relay IDs are switched.
- Add robust local IP resolution for relay-device bindings, including caching and inventory backfill.
- Introduce fast-path options for relay status and switch dispatch.
- Validate PHP extensions (`curl`, `sqlite3`) in edge agent images.
This commit is contained in:
Jeppe Bundgaard
2026-04-27 16:02:36 +02:00
parent 7bd940de37
commit f2e2b9a8f4
25 changed files with 938 additions and 81 deletions
@@ -441,12 +441,14 @@ class moduleSelfServeRoute
$lane = $selfserve->lane($lane_id);
try {
$this->applyShellyTransportOverride($lane);
$lane->open($gate);
$response->success([
'lane_id' => $lane_id,
'gate' => $gate->name,
'opened' => true,
'state' => $lane->getLaneState()->name,
'transport' => $this->requestedShellyTransportOverride(),
]);
} catch (\Exception $e) {
error_log('Failed to open self-serve lane gate ' . $gate->name . ' for lane ' . $lane_id . ': ' . $e->getMessage());
@@ -468,6 +470,7 @@ class moduleSelfServeRoute
$lane = $selfserve->lane($lane_id);
try {
$this->applyShellyTransportOverride($lane);
$status = $lane->getMachineProgramPickerRelayStatus();
$response->success([
'lane_id' => $lane_id,
@@ -475,6 +478,7 @@ class moduleSelfServeRoute
'relay_id' => (string)$status['relay_id'],
'online' => (bool)$status['online'],
'on' => (bool)$status['on'],
'transport' => $this->requestedShellyTransportOverride(),
]);
} catch (\Exception $e) {
$response->error('Failed to get MACHINE_PROGRAM_PICKER relay status: ' . $e->getMessage(), 400);
@@ -501,6 +505,7 @@ class moduleSelfServeRoute
$lane = $selfserve->lane($lane_id);
try {
$this->applyShellyTransportOverride($lane);
$lane->setMachineProgramPickerRelayStatus((bool)$on);
$status = $lane->getMachineProgramPickerRelayStatus();
$response->success([
@@ -510,6 +515,7 @@ class moduleSelfServeRoute
'relay_id' => (string)$status['relay_id'],
'online' => (bool)$status['online'],
'on' => (bool)$status['on'],
'transport' => $this->requestedShellyTransportOverride(),
]);
} catch (\Exception $e) {
$response->error('Failed to set MACHINE_PROGRAM_PICKER relay status: ' . $e->getMessage(), 400);
@@ -530,6 +536,7 @@ class moduleSelfServeRoute
$lane = $selfserve->lane($lane_id);
try {
$this->applyShellyTransportOverride($lane);
$status = $lane->getMachineCleanerRelayStatus();
$response->success([
'lane_id' => $lane_id,
@@ -537,6 +544,7 @@ class moduleSelfServeRoute
'relay_id' => (string)$status['relay_id'],
'online' => (bool)$status['online'],
'on' => (bool)$status['on'],
'transport' => $this->requestedShellyTransportOverride(),
]);
} catch (\Exception $e) {
$response->error('Failed to get MACHINE_CLEANER relay status: ' . $e->getMessage(), 400);
@@ -563,6 +571,7 @@ class moduleSelfServeRoute
$lane = $selfserve->lane($lane_id);
try {
$this->applyShellyTransportOverride($lane);
$lane->setMachineCleanerRelayStatus((bool)$on);
$status = $lane->getMachineCleanerRelayStatus();
$response->success([
@@ -572,6 +581,7 @@ class moduleSelfServeRoute
'relay_id' => (string)$status['relay_id'],
'online' => (bool)$status['online'],
'on' => (bool)$status['on'],
'transport' => $this->requestedShellyTransportOverride(),
]);
} catch (\Exception $e) {
$response->error('Failed to set MACHINE_CLEANER relay status: ' . $e->getMessage(), 400);
@@ -592,6 +602,7 @@ class moduleSelfServeRoute
$lane = $selfserve->lane($lane_id);
try {
$this->applyShellyTransportOverride($lane);
$status = $lane->getMachineRelayStatus();
$response->success([
'lane_id' => $lane_id,
@@ -599,6 +610,7 @@ class moduleSelfServeRoute
'relay_id' => (string)$status['relay_id'],
'online' => (bool)$status['online'],
'on' => (bool)$status['on'],
'transport' => $this->requestedShellyTransportOverride(),
]);
} catch (\Exception $e) {
$response->error('Failed to get MACHINE relay status: ' . $e->getMessage(), 400);
@@ -625,6 +637,7 @@ class moduleSelfServeRoute
$lane = $selfserve->lane($lane_id);
try {
$this->applyShellyTransportOverride($lane);
$lane->setMachineRelayStatus((bool)$on);
// Keep lane cache state aligned with the latest explicit relay action
try {
@@ -651,6 +664,7 @@ class moduleSelfServeRoute
'relay_id' => (string)$status['relay_id'],
'online' => (bool)$status['online'],
'on' => (bool)$status['on'],
'transport' => $this->requestedShellyTransportOverride(),
]);
} catch (\Exception $e) {
$response->error('Failed to set MACHINE relay status: ' . $e->getMessage(), 400);
@@ -984,4 +998,47 @@ class moduleSelfServeRoute
'modules_selfserve_lane_force_machine_disable' => 'Force disable MACHINE relay but keep lane as in-wash (superusers only)'
]);
}
/**
* @throws \Exception
*/
private function applyShellyTransportOverride(object $lane): void
{
$transport = $this->requestedShellyTransportOverride();
if ($transport === null) {
return;
}
if (!method_exists($lane, 'setShellyTransportOverride')) {
throw new \Exception('This lane does not support Shelly transport overrides.');
}
$lane->setShellyTransportOverride($transport);
}
private function requestedShellyTransportOverride(): ?string
{
$transport = null;
foreach (['transport', 'test_transport', 'transport_mode'] as $parameter) {
if (self::isParametersSet([$parameter])) {
$transport = self::getParameter($parameter);
break;
}
}
$normalized = strtolower(trim((string)$transport));
if ($normalized === '') {
return null;
}
if ($normalized === 'gateway') {
return 'local';
}
if (!in_array($normalized, ['local', 'cloud'], true)) {
throw new \InvalidArgumentException('Invalid transport. Expected local or cloud.');
}
return $normalized;
}
}