Damit funktioniert das Login. Leider ist mein freies Kontingent aufgebraucht und ich muss 4,5 Stunden warten 
Devicelist funktioniert noch nicht.
<?php
class AiperIrriSense
{
private $email;
private $password;
private $baseUrl = 'https://apieurope.aiper.com';
private $token = '';
private $tokenExpire = 0;
private $lastKey = '';
private $lastIv = '';
public function __construct($email, $password)
{
$this->email = $email;
$this->password = $password;
}
/* ---------------------------------------------------- */
/* LOGIN */
/* ---------------------------------------------------- */
public function Login()
{
$res = $this->Call('/login', [
'email' => $this->email,
'password' => $this->password
], false);
if (!isset($res['code']) || $res['code'] != 200) {
throw new Exception('Login fehlgeschlagen');
}
$this->token = $res['data']['token'];
$this->tokenExpire = time() + intval($res['data']['tokenExpires']);
if (!empty($res['data']['domain'][0])) {
$this->baseUrl = rtrim($res['data']['domain'][0], '/');
}
return true;
}
private function EnsureLogin()
{
if (!$this->token || time() > ($this->tokenExpire - 300)) {
$this->Login();
}
}
/* ---------------------------------------------------- */
/* GERÄTE */
/* ---------------------------------------------------- */
public function GetDevices()
{
$this->EnsureLogin();
return $this->Call('/equipment/getEquipmentList', []);
}
public function GetEquipmentInfo($sn)
{
$this->EnsureLogin();
return $this->Call('/equipment/getEquipmentInfo', [
'sn' => $sn
]);
}
public function CheckOnline($sn)
{
$this->EnsureLogin();
return $this->Call('/equipment/checkEquipmentOnlineStatus', [
'sn' => $sn
]);
}
/* ---------------------------------------------------- */
/* IRRISENSE */
/* ---------------------------------------------------- */
public function GetZoneMap($sn)
{
$this->EnsureLogin();
return $this->Call('/wr/getMapList', [
'sn' => $sn
]);
}
public function StartZone($sn, $mapId, $waterYield = 0.1)
{
$this->EnsureLogin();
return $this->Call('/wr/setWorkMode', [
'sn' => $sn,
'mode' => 1,
'map_id' => intval($mapId),
'status' => 1,
'waterYield' => $waterYield
]);
}
public function StopZone($sn, $mapId)
{
$this->EnsureLogin();
return $this->Call('/wr/setWorkMode', [
'sn' => $sn,
'mode' => 0,
'map_id' => intval($mapId),
'status' => 0
]);
}
/* ---------------------------------------------------- */
/* API CALL */
/* ---------------------------------------------------- */
private function Call($path, $body = [], $useToken = true)
{
$enc = $this->EncryptPayload($body);
$headers = [
'Content-Type: application/json',
'version: 3.3.0',
'os: ios',
'platform: ios',
'appType: aiper',
'language: en',
'charset: UTF-8',
'requestidkey: K6!R]y_]Q!gA,5vy',
'encryptKey: '.$enc['header']
];
if ($useToken && $this->token) {
$headers[] = 'token: ' . $this->token;
} else {
$headers[] = 'token:';
}
$ch = curl_init($this->baseUrl . $path);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $enc['body'],
CURLOPT_HTTPHEADER => $headers,
CURLOPT_TIMEOUT => 30
]);
$response = curl_exec($ch);
if ($response === false) {
throw new Exception(curl_error($ch));
}
$http = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$type = curl_getinfo($ch, CURLINFO_CONTENT_TYPE);
curl_close($ch);
$decoded = $this->DecryptResponse($response);
echo "PATH: ".$path.PHP_EOL;
echo "RAW:".PHP_EOL;
var_dump($response);
echo PHP_EOL."DECRYPTED:".PHP_EOL;
var_dump($decoded);
echo PHP_EOL."JSON ERROR: ".json_last_error_msg().PHP_EOL;
die();
}
private function DecryptResponse($cipherText)
{
$raw = base64_decode($cipherText);
$plain = openssl_decrypt(
$raw,
'AES-128-CBC',
$this->lastKey,
OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING,
$this->lastIv
);
$plain = rtrim($plain, "\0");
return $plain;
}
/* ---------------------------------------------------- */
/* ENCRYPTION */
/* ---------------------------------------------------- */
private function EncryptPayload($body)
{
$chars = '';
for ($i = 40; $i <= 126; $i++) {
$chars .= chr($i);
}
$key = '';
$iv = '';
for ($i = 0; $i < 16; $i++) {
$key .= $chars[random_int(0, strlen($chars) - 1)];
$iv .= $chars[random_int(0, strlen($chars) - 1)];
}
$payload = [
'timestamp' => round(microtime(true) * 1000),
'nonce' => substr(md5(mt_rand()),0,4)
];
foreach($body as $k=>$v){
$payload[$k]=$v;
}
$json = json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
$pad = 16 - (strlen($json) % 16);
if ($pad < 16) {
$json .= str_repeat(chr(0), $pad);
}
$encrypted = openssl_encrypt(
$json,
'AES-128-CBC',
$key,
OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING,
$iv
);
$pubKeyPem = <<<KEY
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCIKoKPqwq1f60hm/2lpHDF/DT4
J9YaptuTq78nsxdgnSBAvkIZ3E8dqbEBT/VETjJ9Yr28QtHX13E8QGByYxLzYPld
HNXChgOWfSemTEC3TxPvlaSuM9eFUuhqSeGbgoKG7JJNlgjvsPO2cHEhPXJE4qWt
KEZVOZBxEeCgAaLZxwIDAQAB
-----END PUBLIC KEY-----
KEY;
$pubKey = openssl_pkey_get_public($pubKeyPem);
if ($pubKey === false) {
throw new Exception('Public Key konnte nicht geladen werden');
}
$keyData = json_encode([
'key' => $key,
'iv' => $iv
], JSON_UNESCAPED_SLASHES);
$this->lastKey = $key;
$this->lastIv = $iv;
openssl_public_encrypt(
$keyData,
$rsaEncrypted,
$pubKey,
OPENSSL_PKCS1_PADDING
);
return [
'header' => base64_encode($rsaEncrypted),
'body' => json_encode([
'data' => base64_encode($encrypted)
])
];
}
}
/* ---------------------------------------------------- */
/* AUSFÜHRUNG */
/* ---------------------------------------------------- */
$aiper = new AiperIrriSense(
'MAILADRESSE',
'PASSWORT'
);
$aiper->Login();
print_r($aiper->GetDevices());