homeos/app/Support/Shelly/ShellyRpc.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';
}
}