$konfiguration * @return array */ public static function statusAnfrage(int $geraetetyp, array $konfiguration): array { if ($geraetetyp === self::GO_E_ALT) { return self::anfrage('GET', self::lokaleBasisUrl($konfiguration) . '/mqtt?payload='); } if ($geraetetyp === self::GO_E_GEMINI) { return self::anfrage('GET', self::lokaleBasisUrl($konfiguration) . '/api/status'); } if ($geraetetyp === self::SMART_ME_PICO) { $id = self::pflichtwert($konfiguration, 'GeraeteID'); return self::anfrage( 'GET', 'https://api.smart-me.com/pico/charging/' . rawurlencode($id), $konfiguration ); } throw new InvalidArgumentException('Unbekannter Geraetetyp.'); } /** * @param array $konfiguration * @return list> */ public static function steuerAnfragen( int $geraetetyp, array $konfiguration, int $ladestrom ): array { if ($ladestrom < 0 || $ladestrom > 32 || ($ladestrom > 0 && $ladestrom < 6)) { throw new InvalidArgumentException('Ladestrom muss 0 oder 6 bis 32 A betragen.'); } if ($geraetetyp === self::GO_E_ALT) { $basis = self::lokaleBasisUrl($konfiguration) . '/mqtt?payload='; if ($ladestrom === 0) { return [self::anfrage('GET', $basis . 'alw=0')]; } return [ self::anfrage('GET', $basis . 'alw=1'), self::anfrage('GET', $basis . 'amp=' . $ladestrom), ]; } if ($geraetetyp === self::GO_E_GEMINI) { $basis = self::lokaleBasisUrl($konfiguration) . '/api/set?'; if ($ladestrom === 0) { return [self::anfrage('GET', $basis . 'frc=1&sua=false')]; } return [ self::anfrage('GET', $basis . 'frc=2'), self::anfrage('GET', $basis . 'amp=' . $ladestrom), ]; } if ($geraetetyp === self::SMART_ME_PICO) { $seriennummer = self::pflichtwert($konfiguration, 'Seriennummer'); return [self::anfrage( 'POST', 'https://api.smart-me.com/pico/loadmanagementgroup/current/' . rawurlencode($seriennummer) . '?current=' . ($ladestrom * 1000), $konfiguration )]; } throw new InvalidArgumentException('Unbekannter Geraetetyp.'); } /** * @return array{Rohstatus: int, Istleistung_W: float, Sollstrom_A?: int, Maximalstrom_A?: int, Iststrom_A?: float, Phasenzahl?: int} */ public static function dekodiereStatus(int $geraetetyp, string $antwort): array { $daten = json_decode($antwort, true, 512, JSON_THROW_ON_ERROR); if (!is_array($daten)) { throw new RuntimeException('Die Geraeteantwort ist kein JSON-Objekt.'); } if ($geraetetyp === self::GO_E_ALT || $geraetetyp === self::GO_E_GEMINI) { if (!isset($daten['car'], $daten['nrg']) || !is_numeric($daten['car']) || !is_array($daten['nrg']) || !isset($daten['nrg'][11]) || !is_numeric($daten['nrg'][11]) ) { throw new RuntimeException('Die go-e-Antwort enthaelt nicht alle Statusfelder.'); } $leistungsfaktor = $geraetetyp === self::GO_E_ALT ? 10.0 : 1.0; $status = [ 'Rohstatus' => (int) $daten['car'], 'Istleistung_W' => (float) $daten['nrg'][11] * $leistungsfaktor, ]; if (isset($daten['amp']) && is_numeric($daten['amp'])) { $sollstrom = (int) $daten['amp']; $freigabeEntzogen = isset($daten['alw']) && $daten['alw'] === false; $erzwungenAus = $geraetetyp === self::GO_E_GEMINI && isset($daten['frc']) && (int) $daten['frc'] === 1; $status['Sollstrom_A'] = $freigabeEntzogen || $erzwungenAus ? 0 : $sollstrom; } if (isset($daten['ama']) && is_numeric($daten['ama'])) { $maximalstrom = (int) $daten['ama']; if ($maximalstrom >= 6 && $maximalstrom <= 32) { $status['Maximalstrom_A'] = $maximalstrom; } } if (isset($daten['nrg'][4], $daten['nrg'][5], $daten['nrg'][6]) && is_numeric($daten['nrg'][4]) && is_numeric($daten['nrg'][5]) && is_numeric($daten['nrg'][6]) ) { $stromfaktor = $geraetetyp === self::GO_E_ALT ? 0.1 : 1.0; $phasenstroeme = [ (float) $daten['nrg'][4] * $stromfaktor, (float) $daten['nrg'][5] * $stromfaktor, (float) $daten['nrg'][6] * $stromfaktor, ]; $status['Iststrom_A'] = max($phasenstroeme); $aktivePhasen = count(array_filter( $phasenstroeme, static fn (float $strom): bool => $strom >= 1.0 )); if ($aktivePhasen > 0) { $status['Phasenzahl'] = $aktivePhasen >= 2 ? 3 : 1; } } return $status; } if ($geraetetyp === self::SMART_ME_PICO) { if (!isset($daten['State'], $daten['ActiveChargingPower']) || !is_numeric($daten['State']) || !is_numeric($daten['ActiveChargingPower']) ) { throw new RuntimeException('Die Pico-Antwort enthaelt nicht alle Statusfelder.'); } return [ 'Rohstatus' => (int) $daten['State'], 'Istleistung_W' => round((float) $daten['ActiveChargingPower'] * 1000.0), ]; } throw new InvalidArgumentException('Unbekannter Geraetetyp.'); } /** * @param array $konfiguration * @return array */ private static function anfrage( string $methode, string $url, array $konfiguration = [] ): array { $anfrage = ['Methode' => $methode, 'URL' => $url]; if (str_starts_with($url, 'https://api.smart-me.com/')) { $anfrage['Benutzername'] = self::pflichtwert($konfiguration, 'Benutzername'); $anfrage['Passwort'] = self::pflichtwert($konfiguration, 'Passwort'); } return $anfrage; } /** @param array $konfiguration */ private static function lokaleBasisUrl(array $konfiguration): string { $adresse = trim(self::pflichtwert($konfiguration, 'Geraeteadresse')); if (str_contains($adresse, '/') || str_contains($adresse, '?')) { throw new InvalidArgumentException('Geraeteadresse muss ein Hostname oder eine IP-Adresse sein.'); } return 'http://' . $adresse; } /** @param array $konfiguration */ private static function pflichtwert(array $konfiguration, string $name): string { $wert = trim($konfiguration[$name] ?? ''); if ($wert === '') { throw new InvalidArgumentException($name . ' darf nicht leer sein.'); } return $wert; } } final class LadestationTransport { /** @var callable(array): string */ private $anfrageAusfuehren; /** @param callable(array): string $anfrageAusfuehren */ public function __construct(callable $anfrageAusfuehren) { $this->anfrageAusfuehren = $anfrageAusfuehren; } /** @param array $konfiguration */ public function statusLesen(int $geraetetyp, array $konfiguration): string { return ($this->anfrageAusfuehren)( LadestationAdapter::statusAnfrage($geraetetyp, $konfiguration) ); } /** @param array $konfiguration */ public function ladestromSetzen( int $geraetetyp, array $konfiguration, int $ladestrom ): void { foreach (LadestationAdapter::steuerAnfragen($geraetetyp, $konfiguration, $ladestrom) as $anfrage) { ($this->anfrageAusfuehren)($anfrage); } } }