68 lines
1.8 KiB
PHP
68 lines
1.8 KiB
PHP
<?php
|
|
|
|
namespace App\Support\Shelly;
|
|
|
|
use Illuminate\Support\Facades\Http;
|
|
|
|
/**
|
|
* Minimal JSON-RPC client for a Shelly Gen2+ device's LOCAL API (`POST http://<ip>/rpc`).
|
|
* This is the same transport Home Assistant uses — no MQTT setup on the device, just its IP.
|
|
* Vendor specifics stay here (H3).
|
|
*/
|
|
class ShellyRpc
|
|
{
|
|
/**
|
|
* @param array<string,mixed> $params
|
|
* @return array<string,mixed>
|
|
*/
|
|
public function call(string $ip, string $method, array $params = []): array
|
|
{
|
|
$body = ['id' => 1, 'src' => 'homeos', 'method' => $method];
|
|
if ($params !== []) {
|
|
$body['params'] = $params;
|
|
}
|
|
|
|
$response = Http::timeout(5)->connectTimeout(3)->acceptJson()->post($this->url($ip), $body);
|
|
$json = $response->json();
|
|
|
|
if (! is_array($json)) {
|
|
throw new \RuntimeException("Shelly {$ip}: invalid RPC response.");
|
|
}
|
|
|
|
if (isset($json['error'])) {
|
|
throw new \RuntimeException("Shelly {$ip} RPC error: ".json_encode($json['error']));
|
|
}
|
|
|
|
return $json['result'] ?? [];
|
|
}
|
|
|
|
/** Full component status keyed by component ("switch:0", "input:0", …). */
|
|
public function status(string $ip): array
|
|
{
|
|
return $this->call($ip, 'Shelly.GetStatus');
|
|
}
|
|
|
|
/** Device identity: {id, model, gen, name, ver, …}. */
|
|
public function info(string $ip): array
|
|
{
|
|
return $this->call($ip, 'Shelly.GetDeviceInfo');
|
|
}
|
|
|
|
/** True if the device answers RPC (used to validate a manual IP before onboarding). */
|
|
public function reachable(string $ip): bool
|
|
{
|
|
try {
|
|
$this->info($ip);
|
|
|
|
return true;
|
|
} catch (\Throwable) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private function url(string $ip): string
|
|
{
|
|
return 'http://'.$ip.'/rpc';
|
|
}
|
|
}
|